How to use STRING_AGG in C# winForms? - c#

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.

Related

SQL timeout occurs while getting the count after filter/search the dynamically modifed value in the IQueryable

In our project we have used the below code to populate the data to a table. While getting the count after filtering or searching the below highlighted "Exceeded" field we are getting the timeout from SQL .
Screenshot attached
IQueryable data = _context.Orders;
IQueryable<SelectList> dataSource = (from t in _context.Orders
select new
{
t.OrderID,
t.CustomerID,
t.EmployeeID,
t.ShipCity,
CustomerName = t.Employees.FirstName,
EmployeeName = t.Employees.FirstName + " " + t.ShipCity,
cashier = _context.Orders.Where(c => c.OrderID == t.OrderID).Select(c => c.CustomerID + " " + c.ShipCity)
}).Select(t => new SelectList()
{
CustomerName = t.CustomerName,
ShipCity = t.ShipCity,
EmployeeName = t.EmployeeName,
**Exceeded = t.EmployeeID > 5 ? "Yes" : "No"**
}).AsQueryable();

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());

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(") ");
}

DataTable DefaultView Filter String with "Convert()"

Hi I am using DataTable and DevaultView.RowFilter to display a DataGridView.
I have a column with date (dd/mm/yyyy).
String filter = String.Format(#"account_name LIKE '{0}*' AND type_name='{2}' AND Convert( transaction_date, System.DateTime ) < '{1}' OR is_edited ='true' ", this.accountName, this.dateTimePickerTransactionFrom.Value.ToString(), this.radioButtonDebit.Checked ? "Debit" : "Credit");
transactionsAll.DefaultView.RowFilter = filter;
DefaultView_ListChanged();
It ends up in an error:
String was not recognized as a valid DateTime.
Thanks Guys,
I solved the problem.
I changed the DB Query from
SELECT transactions.transaction_id, transactions.transaction_amound, CONVERT(char(10), transactions.transaction_date, 103) AS transaction_date,
transaction_type.type_name, transaction_type.description, accounts.account_name, accounts.user_id
FROM transactions INNER JOIN
transaction_type ON transactions.type_id = transaction_type.type_id INNER JOIN
accounts ON transactions.account_id = accounts.account_id
WHERE (accounts.user_id = #UserId)
To
SELECT transactions.transaction_id, transactions.transaction_amound, transactions.transaction_date, transaction_type.type_name,
transaction_type.description, accounts.account_name, accounts.user_id
FROM transactions INNER JOIN
transaction_type ON transactions.type_id = transaction_type.type_id INNER JOIN
accounts ON transactions.account_id = accounts.account_id
WHERE (accounts.user_id = #UserId)
and
private void ConstraintChanged()
{
String filter = String.Format(#"account_name LIKE '{0}*' AND type_name='{2}' " +
#"AND Convert( transaction_date, System.DateTime ) > '{1}' OR is_edited ='true' ",
this.accountName,
this.dateTimePickerTransactionFrom.Value,
this.radioButtonDebit.Checked ? "Debit" : "Credit");
transactionsAll.DefaultView.RowFilter = filter;
DefaultView_ListChanged();
}

help with a three table linq query

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
}
}

Categories

Resources