I calculate outstanding customers balance in C# Winforms. The code below works, but it's slow. Is there any way to improve its performance?
public DataTable GetOutStandingCustomers()
{
decimal Tot = 0;
DataTable table = new DataTable();
SqlConnection con = null;
try
{
table.Columns.Add("Code", typeof(Int32));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("City", typeof(string));
table.Columns.Add("Tot", typeof(decimal));
string constr = ConfigHelper.GetConnectionString();
string query = "SELECT Code, Name,City FROM Chart WHERE LEFT(CODE,3)='401' AND Code > 401001 ";
string query0 = " SELECT(SELECT ISNULL( SUM(SalSum.Grand),'0' ) FROM SalSum WHERE SalSum.Code = @Code ) +( SELECT ISNULL(SUM(Journals.Amount),'0' ) FROM Journals WHERE Journals.DrCode = @Code ) -( SELECT ISNULL(SUM(RSalSum.Grand),'0' ) FROM RSalSum WHERE RSalSum.Code = @Code ) -( SELECT ISNULL(SUM(Journals.Amount),'0' ) FROM Journals WHERE Journals.CrCode = @Code )+(SELECT ISNULL(SUM(Chart.Debit),'0' ) FROM Chart WHERE Chart.Code = @Code) - (SELECT ISNULL(SUM(Chart.Credit), '0') FROM Chart WHERE Chart.Code = @Code)";
Person per = new Person();
con = new SqlConnection(constr);
SqlCommand com = new SqlCommand(query, con);
SqlCommand com0 = new SqlCommand(query0, con);
con.Open();
SqlDataReader r = com.ExecuteReader();
if (r.HasRows)
{
while (r.Read())
{
per.Name = Convert.ToString(r["Name"]);
per.City = Convert.ToString(r["City"]);
per.Code = Convert.ToString(r["Code"]);
com0.Parameters.Clear();
com0.Parameters.Add("@Code", SqlDbType.Int).Value = per.Code;
Tot = Convert.ToDecimal(com0.ExecuteScalar());
if (Tot != 0)
{
table.Rows.Add(per.Code, per.Name, per.City, Tot);
}
}
}
r.Close();
con.Close();
return table;
}
catch (Exception)
{
throw new Exception();
}
}
In this case it seems you're looping through and performing multiple queries using multiple Codes, you're also querying Chart twice. In this case you'd want to use a LEFT JOIN
from Chart to your other tables.
ON Chart.Code = Salsum.Code
ON Chart.Code = Journal.Code
for example. You will have to look at GROUP BY
as well because you're aggregating some table columns by using SUM
. You may also need to make sure that Code is indexed on the tables you're querying. As long as Code is often queried like this and comparatively rarely Updated or Inserted to, then indexing the Code column on these tables is probably appropriate. Left Joins : https://technet.microsoft.com/en-us/library/ms187518(v=sql.105).aspx Indexing: https://technet.microsoft.com/en-us/library/jj835095(v=sql.110).aspx
Sorry I wrote a book on you here, but optimization often leads to a long answer (especially with SQL).
tldr; Use a LEFT JOIN, grouping by Code
Index the Code columns
The performance problem is due to you retrieve all data from the server and filter data in the client using the complex computed expression that sum from seven tables:
if (Tot != 0)
{
table.Rows.Add(per.Code, per.Name, per.City, Tot);
}
This represent overhead over network plus you manually add the result to the datatable row by row.
The provided solution do filter in the server based on the computed expression using the CROSS APPLY and auto create the datatable directly from the DataReader.
The benefit of CROSS APPLY is all columns are feasible to the main sql query, so you can filter on ToT column, filtering is done in the server (not the client).
public void SelctChart()
{
string sql2 = @"
select c.Code, c.Name,c.City ,oo.T
from chart c
cross apply
( select c.code,
(
(select ISNULL( SUM(SalSum.Grand),0 ) FROM SalSum WHERE SalSum.Code = c.code )
+( select ISNULL(SUM(j.Amount),0 ) FROM [dbo].[Jornals] j WHERE j.DrCode = c.code)
-( SELECT ISNULL(SUM(RSalSum.Grand),'0' ) FROM RSalSum WHERE RSalSum.Code = c.Code )
-( SELECT ISNULL(SUM(j.Amount),0 ) FROM [dbo].[Jornals] j WHERE j.CrCode = c.code )
+(SELECT ISNULL(SUM( c0.Debit),0 ) FROM [dbo].Chart c0 WHERE c0.Code = c.code)
- (SELECT ISNULL(SUM(c1.Credit), 0) FROM [dbo].Chart c1 WHERE c1.Code = c.code)
)T
) oo
where
oo.T >0
and LEFT(c.CODE,3)='401' AND c.Code > 401001
";
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(sql2, connection);
//in case you pass @code as a a parameter
//command.Parameters.Add("@code", SqlDbType.Int);
//command.Parameters["@code"].Value = code;
try
{
connection.Open();
var reader = command.ExecuteReader();
while (!reader.IsClosed)
{
DataTable dt = new DataTable();
// Autoload datatable
dt.Load(reader);
Console.WriteLine(dt.Rows.Count);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
You can modify the method and pass code as a parameter
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.