仅使用一次行程更新一批记录的最简单方法是啥?

Posted

技术标签:

【中文标题】仅使用一次行程更新一批记录的最简单方法是啥?【英文标题】:What is the easiest way to update a batch of records using only one trip?仅使用一次行程更新一批记录的最简单方法是什么? 【发布时间】:2017-06-20 04:08:07 【问题描述】:

通常,您将在循环内一次执行一个更新。如果您有 100 条记录,您将有 100 次访问服务器,这是不可取的。如何通过单次往返数据库更新一组记录。

using System.Data.SqlClient;

for (int ii = 0; ii < ptList.Length; ii++)    
    sql = @"update [CCVT].[dbo].[_tb_NCVT_Points] set PointDateTime = CONVERT(datetime, '"
    + ptList[ii]._dateDt.ToString("yyyy-MM-dd HH:mm:ss.FFF") + "', 121), PointStatus = '"
    + ptList[ii]._statStr + "', PointValue =" + ptList[ii]._valDoub.ToString() 
    + " WHERE Pointkey = '" + ptList[ii]._pointName + "'; ";
    theActiveConnection.Open();
    SqlCommand cmd = new SqlCommand(sql, theActiveConnection);
    try 
        cmd.ExecuteNonQuery();
        cmd.Dispose();
    
    catch (Exception eX) 
       //handle exceptions
     

请不要对这个问题投反对票。 问题How can I update multiple rows in a table with SQL query? 没有要求一趟,他们的回答也没有一趟!您是否看到 ExecuteNonQuery 操作在循环内?那不是我的问题,也不是我的答案!

foreach (DataGridViewRow row in dataGridView2.Rows)
    
        cm.Parameters["@Qty"].Value = row.Cells[2].Value;
        cm.Parameters["@Description"].Value = row.Cells[3].Value;
        cm.Parameters["@Price"].Value = row.Cells[4].Value;
        cm.ExecuteNonQuery();
    
    cn.Close();

【问题讨论】:

然后只需创建一个批处理 SQL 语句并一次性运行它。或者创建存储过程并将您的数据提供给 DataTable。 How can I update multiple rows in a table with SQL query?的可能重复 您可以尝试使用世界上最快的程序,但如果您不修复称为 Sql Injection 的安全漏洞,您将遇到更大的问题 您可能需要小心处理大型多行批处理作业。关于MSSQL VS mysql 的大型工作绩效似乎有两种不同的共识。 您可以尝试 Bulk Insert 或者您可以使用 xml 来执行此操作,或者您可以通过创建 UserDefined 类型并将 List 传递给存储过程/用户类型来执行此操作。更多关于如何更有效地插入多条记录的谷歌搜索 【参考方案1】:
SqlCommand cmd = new SqlCommand("", theActiveConnection);
StringBuilder sql = new StringBuilder();
for (int ii = 0; ii < ptList.Length; ii++)    
    sql.AppendLine("UPDATE [CCVT].[dbo].[_tb_NCVT_Points]"); 
    sql.AppendLine($"SET PointDateTime = CONVERT(datetime, @PointDateTimeii, 121), PointStatus = @PointStatusii, PointValue = @PointValueii");
    sql.AppendLine($"WHERE Pointkey = '@PointKeyii;");
    cmd.Parameters.AddWithValue($"@PointDateTimeii",ptList[ii]._dateDt.ToString("yyyy-MM-dd HH:mm:ss.FFF"));
    cmd.Parameters.AddWithValue($"@PointStatusii",ptList[ii]._statStr);
    cmd.Parameters.AddWithValue($"@PointValueii",ptList[ii]._valDoub.ToString());
    cmd.Parameters.AddWithValue($"@Pointkeyii",ptList[ii]._pointName);


try 
    cmd.CommandText = sql.ToString();
    theActiveConnection.Open();
    cmd.ExecuteNonQuery();

catch (Exception eX) 
   //handle exceptions
 
finally 
    cmd.Dispose();
    theActiveConnection.Close();

有很多方法可以处理此问题,具体取决于命令的接近程度和不同程度。在你的情况下,我认为这是最好的。

【讨论】:

你听说过一个名为 StringBuilder 的类吗? 我有,而且我确实使用了字符串生成器,我只是重新组织了@JennaLeaf 发布的代码,尽管肯定有更好的方法来构建查询字符串。 为了让你开心@Steve,我加入了一个字符串生成器。 谢谢安德鲁!你是最棒的!!!我昨晚回家了,几乎和你一样,但你的 trycatchfinallydispose() 是最好的!我喜欢你采用序列化参数的方式!谢谢!!干得好。 是的,总是在finally 中处理你的清理,这样如果你需要它在catch 中它仍然存在(无论如何都会在清理之前发生任何异常),如果有清理仍然会发生异常。【参考方案2】:

最好的执行方式是在存储过程中使用表值参数 https://msdn.microsoft.com/en-us/library/bb675163(v=vs.110).aspx

简单的方法是使用 StringBuilder 或 ... 将 SQL 语句与分号连接起来。但请注意,这有长度限制!!!

【讨论】:

前者更具可扩展性,对吗?我还查了 StringBuilder 的最大值。我不认为我会超过那个。不过你以前的方法很棒,我喜欢!我投了赞成票!!! @Jenna:请注意,不是 StringBuilder 引入了限制,而是 SQL Server...msdn.microsoft.com/de-de/library/ms143432.aspx(这里的限制也很高)【参考方案3】:

为什么不使用Dapper - .Net 的简单对象映射器

var list = ptList.select(p => new
        DateTime = p._dateDt,
        Status = p._statStr,
        Value = p._valDoub,
        Key = p._pointName
    );
using(var connection = new SqlConnection...)

  connection.open()
  connection.Execute("update [CCVT].[dbo].[_tb_NCVT_Points] set PointDateTime = @DateTime, PointStatus = @Status, PointValue = @Value
 WHERE Pointkey = @Key", list);

【讨论】:

我还没试过!我使用 Linq 和 nuget,但这是我同事的项目(他是反 linq,反并发症),你放在那里的东西看起来应该可以工作!我投了赞成票。谢谢。【参考方案4】:

Andrew 发布了直接答案,但这取决于您的数据来自哪里。

如果点列表已经在数据库中,并且您正在将其查询到列表中(一个带有循环的 SELECT 以填充列表),然后调用大量更新,您应该直接执行 UPDATE FROM它将 SELECT 和 UPDATE 组合在一个语句中。一次访问服务器,网络上没有不必要的行。

【讨论】:

你的也可以。就像你说的,我没有数以百万计的更新 stmts,所以 Andrew 的解决方案是我正在寻找的,特别是我不喜欢在服务器上创建一个仅合并数百条记录的 tmp 工作表。对于更大批量的更新,我肯定会使用你的。我投票赞成你的答案。另见***.com/questions/2334712/… 如果它已经在数据库中,肯定还有其他方法,但即便如此,Jenna 可能还是希望数据通过她的代码、验证、数据调整运行,但我们不这样做'不知道数据的来源,所以我发布了一个解决方案,一旦值是 OP 提供它们的位置,该解决方案将始终有效。 安德鲁,更新数据库字段的值不在数据库中,而是从某个历史引擎输入的。 @JennaLeaf 我真的不认为它们是,我只是说即使它们是,也可能有理由在将它们放回或放入另一个表之前通过 c# 运行这些值。跨度>

以上是关于仅使用一次行程更新一批记录的最简单方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

获取仅包含文本条目的组合框的选定文本的最简单方法是啥?

复制 activerecord 记录的最简单方法是啥?

更新 mnesia 模式的最简单方法是啥?

在本地运行一次计时器触发的 Azure 函数的最简单方法是啥?

在 python 中设置基本记录器的最简单方法是啥?

识别印度游客的最简单、最轻松的方法是啥?