help with a three table linq query - c#

I have 3 tables that I need to use:
Brand-
BrandID
Name
BrandSource-
BrandID
SourceID
Source-
SourceID
SourceName
Image
So I have a many -many relationship with BrandSource being my intermediate table. I have each Brand column displayed in a table and I made a new column for the source image. Essentially, if there is 5 sources for one brand I need it to show one row for the brand and the 5 different source images in the new column I made.(5 images in one cell).
Since I have joined the three tables it obviously sees that there is 5 rows in the BrandSource table and displays 5 rows of each brand with a single source image in a cell.
I'm sure I could select distinct brands, but that still doesn't solve my problem of how I can get all of the source images for each brand to show up in the same cell.
Here is my linq code:(As you can see there is some info in here that I left out above for brevity).
var join = from b in db.Brands
join bs in db.Brands_Sources on b.BrandID equals bs.BrandID
join sb in db.Sources on bs.SourceID equals sb.SourceID
select new { Brand = b, source = sb.Image, c = b.Description.Length < 204 ? b.Description : b.Description.Substring(0, 204) + "..." };
And here is how i'm using it:
foreach (var result in join)
{
bool a = result.Brand.Active;
string chk = string.Empty;
if (a == true)
chk = "checked='checked'";
else
chk = "";
resultSpan.InnerHtml += "<tr><td><input type='checkbox' " + chk + "></td><td width='1%'><img width='50px' src='" + result.Brand.Image + "'</img></td>" +
"<td>" + result.Brand.Name + "</td><td width='60%'>" + result.c + "</td><td><img src='"+result.source+"'></img></td><td>" + result.Brand.DateCreated + "</td><td>" + result.Brand.DateModified + "</td></tr>";
}

You've got a good start, but I think you'd be better served not doing the triple join yourself. Linq-to-sql can handle the details of that for you. If you back away from the query aspect for a second and start with your desired result, you'll do better. From what I can tell, the object type you want out of this is a list of Brands, and each Brand should contain a list of its sources. Here's how you do it (starting with downloading LinqPad)...
// LinqPad C# statement(s)
var results =
from b in Brands
select new {
Brand = b,
Sources = (
from s in Sources
join xref in BrandSources on s.SourceID equals xref.SourceID
where xref.BrandID == b.BrandID
select s
).ToList()
};
result.Dump(); // show result in LinqPad
LinqPad shows that this executes in a single query, but the guts of assembling your List<Source> in your result object happens behind the scenes. Here's what LinqPad executes:
SELECT [t0].[BrandID], [t0].[Name], [t1].[SourceID], [t1].[SourceName], [t1].[Image], (
SELECT COUNT(*)
FROM [Source] AS [t3]
INNER JOIN [BrandSource] AS [t4] ON [t3].[SourceID] = [t4].[SourceID]
WHERE [t4].[BrandID] = [t0].[BrandID]
) AS [value]
FROM [Brand] AS [t0]
LEFT OUTER JOIN ([Source] AS [t1]
INNER JOIN [BrandSource] AS [t2] ON [t1].[SourceID] = [t2].[SourceID]) ON [t2].[BrandID] = [t0].[BrandID]
And here's some test data for those following along at home:
create table Brand (
BrandID int,
Name varchar(50),
)
create table BrandSource (
BrandID int,
SourceID int
)
create table Source (
SourceID int,
SourceName varchar(50),
[Image] varchar(50)
)
insert into Brand select 1, 'Brand1'
insert into Brand select 2, 'Brand2'
insert into Brand select 3, 'Brand3'
insert into Source select 1, 'Source1', 'src1.gif'
insert into Source select 2, 'Source2', 'src2.jpg'
insert into Source select 3, 'Source3', 'src3.bmp'
insert into Source select 4, 'Source4', 'src4.png'
insert into Source select 5, 'Source5', 'src5.raw'
insert into BrandSource select 1, 1
insert into BrandSource select 1, 2
insert into BrandSource select 1, 3
insert into BrandSource select 2, 2
insert into BrandSource select 2, 4
select * from Brand
select * from BrandSource
select * from Source
Notice that you get an empty list of sources for brand #3 this way, which is what I assume you'd want. Your original query INNER JOINed Brand#3 away.
Finally, here's an example of how you'd use your query result:
foreach (var result in results) {
string chk = (result.Brand.Active ? " checked='checked'" : "");
var buf = new StringBuilder();
buf.Append("<tr>");
buf.AppendFormat("<td><input type='checkbox'{0}></td>", chk);
buf.AppendFormat("<td width='1%'><img width='50px' src='{0}'></img></td>", result.Brand.Image);
buf.AppendFormat("<td>{0}</td>", result.Brand.Name);
buf.Append("<td>");
foreach(var src in result.Sources) {
buf.AppendFormat("<img src='{0}'></img>", src.Image);
}
buf.Append("</td>");
buf.Append("</tr>");
resultSpan.InnerHtml = buf.ToString();
}

Sort the LINQ query by BrandID
then use a variable to keep track if it is a new brand.
int lastBrandID = 0;
string closeHtml = "";
foreach (var result in join)
{
if(result.Brand.BrandID != lastBrandID)
{
resultSpan.InnerHtml += closeHtml;
bool a = result.Brand.Active;
string chk = string.Empty;
if (a == true)
chk = "checked='checked'";
else
chk = "";
resultSpan.InnerHtml += "<tr><td><input type='checkbox' " + chk + "></td><td width='1%'><img width='50px' src='" + result.Brand.Image + "'</img></td>" +
"<td>" + result.Brand.Name + "</td><td width='60%'>" + result.c + "</td><td>";
closeHtml = "</td><td>" + result.Brand.DateCreated + "</td><td>" + result.Brand.DateModified + "</td></tr>";
lastBrandID = result.Brand.BrandID;
}
resultSpan.InnerHtml += "<img src='"+result.source+"'></img>";
}
resultSpan.InnerHtml += closeHtml;
On a side note, use a StringBuilder instead of concatenating your strings.
http://msdn.microsoft.com/en-us/library/2839d5h5%28v=VS.100%29.aspx

You can use GroupBy for this (or group .. by .. into in a query) on your original query thus:
var groups = join.GroupBy(b => b.Brand);
foreach (var group in groups)
{
var brand = group.Key;
foreach (var row in group)
{
// you get the idea
}
}

Related

How to use STRING_AGG in C# winForms?

I tried this code in C# winforms but its not working like MSSQL ,
when i select data and using string_agg in SQL its working , but in C# forms its not working and
show the data on multiple lines
This is the code :
private void BtnSearch_Click(object sender, EventArgs e)
{
if (chkCash.Checked == false && chkCovid.Checked == false)
{
btnCash.Enabled = false;
BtnPrint.Enabled = true;
string sql = #" SELECT distinct a.patient_no as 'File No' ,
a.Patient_name as 'Patient Name' ,
b.order_id as 'Order Id' ,
c.custid as 'Clinic No',
c.custname as 'Clinic Name' ,
e.TestId as 'Test Id',
string_agg(e.testname, ',') as 'Test',
b.order_date as 'Order Date',
b.lab_no as 'Lab No'
FROM patients a , lab_orders b ,customers c , order_details d , labtests e
where a.patient_no = b.patient_no
and b.custid = c.custid
and b.order_id = d.order_id
and d.testid = e.testid
and c.CustId > 1
and d.TESTID <> 6438
and cast(b.order_date as time) between '01:00:00' and '23:50:50' ";
string condition = "";
string orderby = "";
orderby += " ORDER BY c.custid";
string groupby = "";
groupby += " group by a.patient_no,a.Patient_name , b.order_id , c.custid , c.custname , b.order_date , b.lab_no, e.TestId ";
DateTime fromDate;
DateTime toDate;
if (!DateTime.TryParse(dtFromDate.Value.ToString(), out fromDate))
{
System.Windows.Forms.MessageBox.Show("Invalid From Date");
}
else if (!DateTime.TryParse(dtToDate.Value.ToString(), out toDate))
{
System.Windows.Forms.MessageBox.Show("Invalid to Date");
}
else
{
condition += " and cast(b.order_date as date) between '" + fromDate + "' and '" + toDate + "'";
}
DataTable dt = data.fireDatatable(string.Format(sql + condition + groupby + orderby));
OrdersDataGridView.DataSource = dt;
OrdersDataGridView.Refresh();
}
Can I use it in winforms ?
Example :
in SQL SERVER when I run the SELECT the output for
orders and tests like this :
Test order_id
CBC,TSH,LDL 100
In C# when run the above code the output for order multiple lines and not one row for each order in case multiple tests ordered in one order :
Test order_id
CBC 100
TSH 100
LDL 100
Are you sure you want to group by test id ? That would cause each test to not be in the list. Is this the same group by you had in the other system.

Correct query not working using MySqlDataReader

So I'm trying to figure out of this, in particular I have this query that perfectly works using PhpMyAdmin:
SELECT tt.team_id, (CASE WHEN t.id IS NULL THEN 0 ELSE 1 END) as exist FROM(SELECT 13048 as team_id UNION ALL SELECT 17058 UNION ALL SELECT 38809 UNION ALL SELECT 8216 UNION ALL SELECT 5466) tt LEFT JOIN team t on t.id = tt.team_id WHERE t.id IS NULL OR t.update_at < DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)
Anyway, I get this error from Visual Studio:
MySql.Data.MySqlClient.MySqlException: 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' UNION ALL SELECT 17058 UNION ALL SELECT 38809 UNION ALL SELECT 8216 UNION ALL ' at line 1'
This error is retured on:
using (MySqlDataReader reader = command.ExecuteReader())
I setup the query in this way:
command.CommandText = "SELECT tt.team_id, " +
"(CASE WHEN t.id IS NULL THEN 0 ELSE 1 END) as exist " +
"FROM(SELECT #first as team_id #others) tt LEFT JOIN team t on t.id = tt.team_id " +
"WHERE t.id IS NULL OR " +
"t.update_at < DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)";
command.Parameters.Add("#first", MySqlDbType.Int32).Value = teams.First().Id;
command.Parameters.Add("#others", MySqlDbType.String).Value = string.Concat(teams.Skip(1).Select(c => " UNION ALL SELECT " + c.Id));
Someone could help me?
This is how I would build a dynamic list of parameters to pass to your query.
Warning, not tested, but this should produce the expected output
// Command text with a placeholder where we insert the dynamic text
string cmd = #"SELECT tt.team_id,
(CASE WHEN t.id IS NULL THEN 0 ELSE 1 END) as exist
FROM (SELECT {texttoreplace}) tt
LEFT JOIN team t on t.id = tt.team_id WHERE t.id IS NULL
OR t.update_at < DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)";
int prmCounter = 1;
// Where we keep the text to insert at the appropriate place
StringBuilder unions = new StringBuilder();
// Where we keep the parameters to add at the MySqlCommand
List<MySqlParameter> prms = new List<MySqlParameter>();
// First parameter
MySqlParameter pr = new MySqlParameter("#first", MySqlDbType.Int32) { Value = teams.First().id};
prms.Add(pr);
unions.Append($" #first as team_id ");
// Loop over your IDs and build parameters and text
foreach (var t in teams.Skip(1))
{
// Giving an unique name to the parameter
string placeholder = "#p" + prmCounter;
unions.Append($" UNION ALL SELECT {placeholder}");
pr = new MySqlParameter(placeholder, MySqlDbType.Int32) { Value = t.id};
prms.Add(pr);
prmCounter++;
}
// Add all the required parameters
command.Parameters.AddRange(prms.ToArray());
// Replace the placeholder with the built text
cmd = cmd.Replace("{texttoreplace}", unions.ToString());

get multiple string outputs from stored procedure using entity framework

The following stored procedure displays three strings and a table row result as output.
Is there any way we can display all the results on a mvc view output panel using entity framework?
I could see the first string result in the code below. But is there anyway to get the other two select string outputs and
the table row result.
private CustomerEntities db = new CustomerEntities();
public ActionResult Index()
{
var results = db.usp_CustomerData("124544", 1500);
var abc = results.ToList();
return View();
}
ALTER PROCEDURE [dbo].[usp_CustomerData]
#CustomerID varchar(6),
#MinsBack int
AS
BEGIN
DECLARE #Count int
SET #Count = (SELECT Count(*)
FROM Customer WITH (NOLOCK)
WHERE CustomerID = #CustomerID AND
DATEDIFF(mi, ReceivedAt, GETUTCDATE()) < #MinsBack)
IF (#Count = 1)
SELECT 'Ok: 1 message in Customer table'
ELSE
SELECT 'ERROR: Expected 1 message in Customer table, but found ' + CONVERT(varchar(3), #Count) + ' messages.'
SET #Count = (SELECT Count(*)
FROM CustomerDetails WITH (NOLOCK)
WHERE CustomerID = #CustomerID AND
DATEDIFF(mi, LastUpdatedAt, GETDATE()) < #MinsBack)
IF (#Count = 1)
SELECT 'Ok: 1 record in CustomerDetails table'
ELSE
SELECT 'ERROR: Expected 1 record in CustomerDetails table, but found ' + CONVERT(varchar(3), #Count) + ' records.'
SET #Count = (SELECT Count(*)
FROM CustomerProduct WITH (NOLOCK)
WHERE CustomerID = #CustomerID AND
DATEDIFF(mi, LastUpdatedAt, GETDATE()) < #MinsBack)
IF (#Count = 1)
SELECT 'Ok: 1 record in CustomerProduct table'
ELSE
SELECT 'ERROR: Expected 1 record in CustomerProduct table, but found ' + CONVERT(varchar(3), #Count) + ' records.'
SELECT *FROM Customer where customerID = #CustomerID
END
As suggestion you could create a temporary table in your SQL script which will be used as temporary store.
CREATE TABLE #Results
(
Message VARCHAR(512)
)
Instead of a direct SELECT in each IF or ELSE you should insert the string into the temp table.
At the end you could reach your goal to get all inserted strings to return them by:
SELECT * FROM #Results
To get customers - like you do at the end - you should trigger a new query to database.
Depending on your case you should consider to querying the database by data context instead of querying the database by store procedures.
You need to do something as suggest in this link but I summarized below
For each results set you will need to do a reader.NextResult();
var someReturnObject = new ResultObject();
using (var context = new LinqPadDbContext(#"Server=localhost\SQLEXPRESS;Database=StackOverflow;Trusted_Connection=True;"))
{
var cmd = context.Database.Connection.CreateCommand();
cmd.CommandText = "[dbo].[GetSomeData]";
try
{
context.Database.Connection.Open();
var reader = cmd.ExecuteReader();
var result1 = ((IObjectContextAdapter)context).ObjectContext.Translate<string>(reader);
someResultObject.Text1 = result1.First();
//for each extra result, start here
reader.NextResult();
var users = ((IObjectContextAdapter)context).ObjectContext.Translate<User>(reader);
someResultObject.Users = users.Select(x => x);
//stop here
}
finally
{
context.Database.Connection.Close();
}
}

Error in SQL query when searching

I have an SQL query that displays information from different tables in the database. This query is then displayed in a DataGrid and I have some options in a DropDownList to search through the DataGrid for certain values. The problem is the search doesn't display the correct information for CollectName or DeliverName.
Code for DropDownList:
private static readonly Dictionary<string, string> SearchFields = new Dictionary<string, string> {
{ "Customer", "c.Name" },
{ "Department", "jn.Department" },
{ "CollectName", "SELECT Name FROM job_address WHERE AddressType = 3 AND JobID = jn.ID" },
{ "DeliverName", "(SELECT Name FROM job_address WHERE AddressType = 2 AND JobID = jn.ID)" }
};
In the SQL query CollectName and DeliverName are inner select statements and that's whats causing the problem here because the search for Customer and Department work fine.
The SQL query:
SELECT c.Name,
COUNT(distinct jn.ID) as Jobs,
sum(jn.OutTurn) as Outturn,
SUM(jn.ActualWeight) as GrossWt,
SUM(jn.CBM) as CBM,
jn.Department,
(SELECT Name FROM job_address WHERE AddressType =3 AND JobID = jn.ID) as CollectName,
(SELECT Name FROM job_address WHERE AddressType =2 AND JobID = jn.ID) as DeliverName
FROM customer c
LEFT JOIN job_address ja ON c.AccountCode = ja.Code AND c.Company_ID = ja.Company_ID
JOIN AddressType jat ON ja.AddressType = jat.ID and jat.Description = 'Debtor'
LEFT JOIN job_new jn ON ja.JobID = jn.ID
WHERE c.Company_ID = ?compid
GROUP BY c.ID
I have a search function that takes the value selected from the DropDownList and the value entered in the textbox:
List<MySqlParameter> param = new List<MySqlParameter>{ new MySqlParameter("compid", CompanyID) };
StringBuilder SQL = new StringBuilder(SearchSQL);
if (SearchFieldKey != null && SearchFieldKey.Length > 0)
{
SQL.Append(" AND (");
for (int i = 0; i < SearchFieldKey.Length; i++)
{
if (SearchFields.ContainsKey(SearchFieldKey[i]))
{
SQL.Append(SearchFields[SearchFieldKey[i]] + " LIKE ?parameter" + i.ToString());
param.Add(new MySqlParameter("parameter" + i.ToString(), "%" + SearchTerms[i] + "%"));
if (i != SearchFieldKey.Length - 1)
SQL.Append(" OR ");
}
else
throw new Exception("Error: Attempted to search on invalid field. Check SearchFields Argument.");
}
SQL.Append(") ");
}
So for example I search for a customer, the SQL query get this line added to end:
WHERE c.Company_ID = ?compid AND (c.Name LIKE ?parameter0)
And when I search for CollectName or DeliverName the query is this:
WHERE c.Company_ID = ?compid AND (SELECT Name FROM job_address WHERE AddressType = 3 AND JobID = jn.ID LIKE ?parameter0)
Is there a problem with this SQL query that causes CollectName and DeliverName not to work?
The parenthesis doesn't match, it should be
WHERE c.Company_ID = ?compid
AND (SELECT Name FROM job_address WHERE AddressType = 3 AND JobID = jn.ID) LIKE ?parameter0
To solve this, you can in your dictionary embed the statement:
{ "CollectName", "(SELECT Name FROM job_address WHERE AddressType = 3 AND JobID = jn.ID)" },
Or in your method that build the SQL, embed automatically the subquery:
SQL.Append("(" + SearchFields[SearchFieldKey[i]] + ") LIKE ?parameter" + i.ToString());
Full correction : you should not try to concatenate string together if you are using a StringBuilder:
var param = new List<MySqlParameter> { new MySqlParameter("compid", CompanyID) };
StringBuilder SQL = new StringBuilder(SearchSQL);
if (SearchFieldKey != null && SearchFieldKey.Length > 0)
{
SQL.Append(" AND (");
for (int i = 0; i < SearchFieldKey.Length; i++)
{
if (SearchFields.ContainsKey(SearchFieldKey[i]))
{
SQL.Append("(");
SQL.Append(SearchFields[SearchFieldKey[i]]);
SQL.Append(") LIKE ?parameter");
SQL.Append(i);
param.Add(new MySqlParameter("parameter" + i.ToString(), "%" + SearchTerms[i] + "%"));
if (i != SearchFieldKey.Length - 1)
SQL.Append(" OR ");
}
else
throw new Exception("Error: Attempted to search on invalid field. Check SearchFields Argument.");
}
SQL.Append(") ");
}

How to display the results of inner join query table in sqlite?

So, I'm using C# winrt with SQLite database. I want to display the results of the query which i have "join"ed.
I have 2 tables which are Student and Course.
Student has : id(PK), name, courseid
Course has : courseid(PK), coursename
And here is my code :
var query1 = conn.QueryAsync<Student>("select * from Student s inner Join Course c on s.courseid = c.courseid");
var query2 = conn.QueryAsync<Course>("select * from Course c inner join Student s on c.courseid = s.courseid");
var result1 = await query1;
var result2 = await query2;
lstJoin.Items.Clear();
foreach (var item in result1)
{
string text = "Name: " + item.name + ", Course Id: " + item.courseid + ", Course Name : " + item.coursename;
lstJoin.Items.Add(text);
}
But the "item.coursename" is error, so i can't display it. And then, if I change result1 in foreach with result 2, "item.name" will be error. What should I do so I can display both of them? Thank you.
Why don't you do it in classic way? like SQLiteCommand object with command text:
strCommandText = "SELECT tbl1.*,tbl2.* FROM Table1 tbl1 INNER JOIN Table2 tbl2 ON tbl1.ID = tbl2.ID";
Or is it a specification that you need to use it this way??
Because I had the same issue when I was using the SQLite Helper file.

Categories

Resources