1

I use this code to delete records which is selected by checkbox in the datagridview, bout it take too long time to do the command

private void delete_Click(object sender, EventArgs e)
    {
        foreach(DataGridViewRow item in advancedDataGridView1.Rows)
        {
            if(bool.Parse(item.Cells[0].Value.ToString()))
            {

                conn.Open();
                SqlCommand cmd = new SqlCommand("delete from tabl where id = '" + item.Cells[1].Value.ToString() + "'", conn);
                cmd.ExecuteNonQuery();
                conn.Close();
            }
        }
        MessageBox.Show("Successfully Deleted....");
    }

and I use this code for the checkbox

private void Chkselectall_CheckedChanged(object sender, EventArgs e)
    {
        for(int n = 0; n< advancedDataGridView1.Rows.Count;n++)
        {
            advancedDataGridView1.Rows[n].Cells[0].Value = chkselectall.Checked;
        }
    }

What should I do the solve this problem?

Ilyes
  • 14,640
  • 4
  • 29
  • 55
  • Check generated sql-query and use profiler in sql-server for detecting problem. I think, query execution takes a lot of time – Backs Jan 09 '19 at 09:46
  • At a first glance I would place `conn.Open()` and `conn.Close()` outside of the loop. – Bill Tür stands with Ukraine Jan 09 '19 at 09:48
  • 4
    Your code looks like a great place to play around with SQL injection... Google a bit for "ADO.NET Parameters" – OzrenTkalcecKrznaric Jan 09 '19 at 09:48
  • 2
    first thing try running same query in management studio and see if it takes as long. if yes, then you should look into your table schema/indexes.profiling and checking execution plan would help pinpoint the bottlenecks. plus, placing plain query in code is not safe and poses maintainability issue as well. – Siavash Rostami Jan 09 '19 at 09:49
  • 2
    Yeap, your code is asking about sql injection, way to avoid: https://stackoverflow.com/questions/14376473/what-are-good-ways-to-prevent-sql-injection – Pawel Czapski Jan 09 '19 at 09:50
  • Please don't wrrite code like this: `new SqlCommand("delete from tabl where id = '" + item.Cells[1].Value.ToString() + "'", conn);` **Parametrise your SQL.** [SqlCommand.Parameters Property](https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.parameters?view=netframework-4.7.2) – Thom A Jan 09 '19 at 09:51
  • Apart from the obvious SQL injection risk, the code opens a new connection for each execution. Simply opening the connection *outside* the loop would result in a significant improvement. – Panagiotis Kanavos Jan 09 '19 at 09:52

4 Answers4

5

Well, you execute a bunch of commands in a sequence. For each one, you open a new connection, close it, and fast as it is, there is always an overhead involved. You better get a list of IDs you need to delete and change your command to

delete from tabl where id in (…)
Nick
  • 4,787
  • 2
  • 18
  • 24
  • No. I mean something like `ids = ids + ", " + item.Cells[1].Value.ToString()`, and after the look create a `SqlCommand` – Nick Jan 09 '19 at 11:28
0

Few things that you are doing in wrong way.

  • You are using inline queries, we are already having many threads(in SO)that discussing about this issue, as it opens a wide door for SQLInjection.
  • The reason that slow down the performance of the execution is the way you are processing. ie., you are iterating through each row and for each matching items you are opening a connection and executing the delete query.

I prefer you to use the following steps:

  • Iterate through rows and collect the items to delete based on the condition.
  • construct a prameterized query which accept a parameter of type string.
  • Assign the collected ids as the value for the parameter.
  • Execute the query.

Please find the sample code below:

private void delete_Click(object sender, EventArgs e)
{
    List<string> selectedIds = new List<string>();
    foreach (DataGridViewRow item in advancedDataGridView1.Rows)
    {
        if (bool.Parse(item.Cells[0].Value.ToString()))
        {
            selectedIds.Add("'" + item.Cells[1].Value.ToString() + "'");
            // collecting all ids
        }
    }
    String sql = "delete from tabl where id in(@idsToDelete)";
    using (SqlConnection cn = new SqlConnection("Your connection string here")) 
    {
        cn.Open();
        using (SqlCommand cmd = new SqlCommand(sql, cn)) 
        {
            cmd.Parameters.Add("@idsToDelete", SqlDbType.VarChar).Value = string.Join(",", selectedIds);
            cmd.ExecuteNonQuery();
        }
    }      
}
sujith karivelil
  • 28,671
  • 6
  • 55
  • 88
  • 2
    `IN` will treat the parameter as a *single* value, not a list of values – Panagiotis Kanavos Jan 09 '19 at 09:59
  • Please do not advocate the use of AddWithValue: [AddWithValue is Evil](http://www.dbdelta.com/addwithvalue-is-evil/); [How to pollute your Plan Cache with parameterized SQL statements](https://www.sqlpassion.at/archive/2015/07/20/how-to-pollute-your-plan-cache-with-parameterized-sql-statements/); [AddWithValue is evil!](http://chrisrickard.blogspot.com/2007/06/addwithvalue-is-evil.html)... – Andrew Morton Jan 09 '19 at 10:05
  • @AndrewMorton: Yep..! me too read that article, made those changes. Thank you for the remainder – sujith karivelil Jan 09 '19 at 10:17
  • It still can't work for the [reason Panagiotis gave earlier](https://stackoverflow.com/questions/54107167/the-delete-button-is-too-slow-what-is-the-problem/54107389?noredirect=1#comment95047017_54107389). You could try using a [table-valued parameter](http://www.sommarskog.se/arrays-in-sql-2008.html), but I'm not *certain* that that can be done without a stored procedure. – Andrew Morton Jan 09 '19 at 10:32
  • @AndrewMorton i don't use stored procedure because im new in coding and don't learnt it yet –  Jan 09 '19 at 11:16
  • @sujithkarivelil i get that error (ExecuteNonQuery requires an open and available Connection. The connection's current state is closed) –  Jan 09 '19 at 12:14
  • @AhmedAlKhteeb: The message is clear enough na? forgot to open the connection. check the updated code in my answer – sujith karivelil Jan 09 '19 at 12:16
  • @sujithkarivelil (Conversion failed when converting the varchar value ''1499840'' to data type int) –  Jan 09 '19 at 12:29
  • @sujithkarivelil where is the problem ? –  Jan 13 '19 at 05:30
0

thx all for your help i found what i need to and i hop it help anyone else need something like that

String sql;
int parameterCounter;
SqlParameter parameter;

private void delete_Click(object sender, EventArgs e)
{
    sql = "delete from tabl where id in (";
    parameterCounter = 0;

    using (SqlConnection cn = new SqlConnection("....")) {
    using (SqlCommand cmd = new SqlCommand(sql, cn)) {
    foreach (DataGridViewRow item in advancedDataGridView1.Rows) {
     if (bool.Parse(item.Cells[0].Value.ToString())) {
        parameterCounter++;
        parameter = new SqlParameter();
        parameter.ParameterName = "@par" + parameterCounter.ToString();
        parameter.DbType = System.Data.DbType.Int32;
        parameter.Value = item.Cells[1].Value;
        cmd.Parameters.Add(parameter);
        sql = sql + $"{parameter.ParameterName},";
        // collecting all ids
     }
  }
  sql = sql.TrimEnd(',');
  sql = sql + ")";

  cmd.CommandText = sql;
  cmd.Connection = cn;
  cn.Open();
  cmd.ExecuteNonQuery();
    MessageBox.Show("Successfully Deleted....");
}
-1

You should put the sql operations out of the foreach loop. Concat the sql string in the loop and execute it out of the loop.

private void delete_Click(object sender, EventArgs e)
    {
        StringBuilder sb = new StringBuilder();
        foreach(DataGridViewRow item in advancedDataGridView1.Rows)
        {
            if(bool.Parse(item.Cells[0].Value.ToString()))
            {
                sb.AppendFormat("delete from tabl where id='{0}';{1}", item.Cells[1].Value, Environment.NewLine);

            }
        }
                conn.Open();
                SqlCommand cmd = new SqlCommand(sb.ToString(), conn);
                cmd.ExecuteNonQuery();
                conn.Close();
        MessageBox.Show("Successfully Deleted....");
    }

Theoretically, passing sql parameters directly is quite dangerous because of sql injection. You have to deal with it yourself

ojlovecd
  • 4,812
  • 1
  • 20
  • 22
  • It's actually *easier* to write correct code that uses command parameters instead of parsing. – Panagiotis Kanavos Jan 09 '19 at 09:54
  • @PanagiotisKanavos how is that –  Jan 09 '19 at 09:56
  • @AhmedAlKhteeb Nick already posted a good answer. If you have more than 1000 rows to delete there are other ways – Panagiotis Kanavos Jan 09 '19 at 09:56
  • @PanagiotisKanavos yes i have more the 1000 rows –  Jan 09 '19 at 11:34
  • @AhmedAlKhteeb what are 1K rows doing in a *grid* and how were they selected? The criteria used to select those 1K rows in the grid should probably be used in the query instead. In any case I suggest you update the question. An `IN` clause can't have more than 1K items. You can use a table-valued parameter to pass the IDs and use a JOIN in the `DELETE` statement, or you can insert the IDs into a staging table and join with that – Panagiotis Kanavos Jan 09 '19 at 11:37
  • the user selected it by select all or one by one or make a filter and select all there are many way to do the select . so what is the update should be like ?? –  Jan 09 '19 at 11:44