简体   繁体   中英

MS SQL Query in C# - poor performance

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM