【问题标题】:The delete button is too slow what is the problem删除按钮太慢是什么问题
【发布时间】:2019-01-09 09:45:18
【问题描述】:

我使用此代码删除checkboxdatagridview中选择的记录,但是执行该命令需要很长时间

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

我将此代码用于复选框

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

我应该怎么做才能解决这个问题?

【问题讨论】:

  • 检查生成的 sql-query 并在 sql-server 中使用 profiler 来检测问题。我认为,查询执行需要很多时间
  • 乍一看,我会将conn.Open()conn.Close() 放在循环之外。
  • 你的代码看起来像是一个玩 SQL 注入的好地方...谷歌一下“ADO.NET 参数”
  • 首先尝试在管理工作室中运行相同的查询,看看是否需要很长时间。如果是,那么您应该查看您的表 schema/indexes.profiling 并检查执行计划将有助于查明瓶颈。另外,在代码中放置普通查询是不安全的,并且还存在可维护性问题。
  • 是的,你的代码在询问sql注入,避免方法:stackoverflow.com/questions/14376473/…

标签: c# sql-server database


【解决方案1】:

好吧,你按顺序执行一堆命令。对于每一个,你打开一个新的连接,关闭它,而且速度很快,总是会涉及到开销。您最好获取需要删除的 ID 列表并将您的命令更改为

delete from tabl where id in (…)

【讨论】:

  • 没有。我的意思是ids = ids + ", " + item.Cells[1].Value.ToString(),然后在外观创建SqlCommand
【解决方案2】:

你做错的事情很少。

  • 您正在使用内联查询,我们已经有很多线程(在 SO 中)讨论这个问题,因为它为 SQLInjection 敞开了大门。
  • 降低执行性能的原因是您的处理方式。即,您正在遍历每一行,并为每个匹配的项目打开一个连接并执行删除查询。

我更喜欢你使用以下步骤:

  • 遍历行并根据条件收集要删除的项目。
  • 构造一个接受字符串类型参数的参数化查询。
  • 将收集到的 id 指定为参数的值。
  • 执行查询。

请在下面找到示例代码:

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

【讨论】:

【解决方案3】:

谢谢大家的帮助,我找到了我需要的东西,我希望它能帮助其他需要类似东西的人

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

【讨论】:

    【解决方案4】:

    您应该将 sql 操作置于 foreach 循环之外。将循环中的sql字符串连接起来,在循环外执行。

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

    理论上,直接传递sql参数是非常危险的,因为sql注入。你必须自己处理它

    【讨论】:

    • 编写使用命令参数而不是解析的正确代码实际上更容易
    • @PanagiotisKanavos 怎么样
    • @AhmedAlKhteeb Nick 已经发布了一个很好的答案。如果要删除的行数超过 1000 行,还有其他方法
    • @PanagiotisKanavos 是的,我有更多的 1000 行
    • @AhmedAlKhteeb 1K 行在 grid 中做了什么?它们是如何选择的?用于选择网格中那 1K 行的条件可能应该在查询中使用。无论如何,我建议您更新问题。 IN 子句不能超过 1K 项。您可以使用表值参数来传递 ID 并在 DELETE 语句中使用 JOIN,或者您可以将 ID 插入临时表并与之连接
    猜你喜欢
    • 2017-07-01
    • 1970-01-01
    • 2017-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多