批量更新 SQL 服务器的 C# 代码

Posted

技术标签:

【中文标题】批量更新 SQL 服务器的 C# 代码【英文标题】:C# code to bulk update SQL server 【发布时间】:2013-03-11 04:58:27 【问题描述】:

我们有这个 C# 代码,它将根据结构中的标志更新 SQL 服务器数据库表。

public struct stSRK
 
    public string Sno;
    public string SClaimID;
    public bool SType;
    public string SText; 

结构中的数据一次最多可以包含 5000-10000 条记录。目前我们正在使用以下 C# 代码来更新数据库,它会根据结构进行“n”次数据库调用。将有助于有一种替代方法来使用相同的 struct 进行批量更新。请看下面的方法

public int UpdateClaims(List<stSRK> _lt, string strClaimType)

try

    int iCount = 0;
    for (int i = 0; i < _lt.Count; i++)
        
            if (_lt[i].sType == true)
            
                if (_lt[i].SText == 'A')
                
                iCount += Convert.ToInt16(SQL.ExecuteSQL("UPDATE table SET ClaimType = '" + strClaimType + "' WHERE 
                    (Sno = '" + _lt[i].Sno + "' AND ClaimID = '" + _lt[i].SClaimID + "')"));
                
                else
                
                iCount += Convert.ToInt16(SQL.ExecuteSQL("UPDATE table SET ClaimType = '" + strClaimType + "' WHERE 
                    (Sno = '" + _lt[i].Sno + "' AND ClaimID = '" + _lt[i].SClaimID + "')"));
                
            
            else
            
                if (_lt[i].SText == 'B')
                
                iCount += Convert.ToInt16(SQL.ExecuteSQL("UPDATE table SET ClaimType = '" + strClaimType + "' WHERE 
                    (Sno = '" + _lt[i].Sno + "' AND ClaimID = '" + _lt[i].SClaimID + "')"));
                
                else
                
                iCount += Convert.ToInt16(SQL.ExecuteSQL("UPDATE table SET ClaimType = '" + strClaimType + "' WHERE 
                    (Sno = '" + _lt[i].Sno + "' AND ClaimID = '" + _lt[i].SClaimID + "')"));
                
            
        return iCount;
        
catch (Exception e)

    throw e.Message;

【问题讨论】:

【参考方案1】:

您的查询似乎在所有情况下都是相同的..

你不能重写你的代码来只构建一个 SQL 语句吗?

类似这样的:

public int UpdateClaims(List<stSRK> _lt, string strClaimType)

    try
    
        string allSno = String.Join(",", _lt.Select(l=> l.Sno.ToString()).ToArray());
        string allClaimID = String.Join(",", _lt.Select(l=> l.SClaimID.ToString()).ToArray());

        // Warning: NOT Production code, SQLInjection hazard!
        return Convert.ToInt16(SQL.ExecuteSQL("UPDATE table SET ClaimType = '" + strClaimType + @"' WHERE 
                        (Sno IN(" + allSno + ") AND ClaimID IN(" + allClaimID + "))"));
    catch (Exception e)
    
        //This is not a good idea, as this way you loose all innerException 
        // information about the exception. And you might just want to remove 
        // the try-catch alltogether if you just rethrow the exception.
        throw e.Message;
    

【讨论】:

值得注意的是:这种方法不能很好地扩展到更大的更新 - 理想情况下应该是一种混合方法,您可以批量处理 1,000 条记录(或任何对您正在处理的记录/环境类型有意义的方法和)。如果您使用 200,000 条记录尝试上述方法,SQL Server 可能会在查询解析时严重窒息。 另外,你提出的where子句可能存在逻辑缺陷:如果我想删除Sno=1 AND ClaimID=2的记录,我想删除Sno=2 AND ClaimID=1的另一记录,我也会不小心正在删除 Sno=1 AND ClaimID=1Sno=2 AND ClaimID=2 的记录。 谢谢陶,你说的很对,我应该在回答中提到这一点。我跳到了一个可能是错误的假设,即它们总是独一无二的。由 OP 将其置于上下文中,因为我不知道 Sno 是什么意思。 上面写满了 SQL 注入。虽然它只是对 OP 代码的重构,但我真的建议您自己的答案参数化查询。答案有一种被别人当作福音的方式。【参考方案2】:

我的代码:

 DataTable tblDetails = new DataTable("tblPlanDetail");

        tblDetails.Columns.Add("row1").DataType = typeof(Int32);
        tblDetails.Columns.Add("row2").DataType = typeof(DateTime);
        tblDetails.Columns.Add("row3").DataType = typeof(String); ;
        tblDetails.Columns.Add("row4").DataType = typeof(Int32); ;


        for (int i = 0; i < table.Rows.Count; i++)
        

            for (int j = 1; j <= DateTime.DaysInMonth(monthYear.Year, monthYear.Month); j++)
            
                DataRow row = tblDetails.NewRow();

                DateTime DayOfMonth = new DateTime(monthYear.Year, monthYear.Month, j);
                row["row1"] = idPlan;
                row["row2"] = DayOfMonth;
                row["row3"] = table.Rows[i][0];
                row["row4"] = Int32.Parse((string)table.Rows[i][j]);

                tblDetails.Rows.Add(row);
            
        

        try
        

            SqlBulkCopy sqlbulk = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction);
            sqlbulk.ColumnMappings.Add("row1", "row1");
            sqlbulk.ColumnMappings.Add("row2", "row2");
            sqlbulk.ColumnMappings.Add("row3", "row3");
            sqlbulk.ColumnMappings.Add("row4", "row4");

            sqlbulk.DestinationTableName = "tblPlanDescription";
            sqlbulk.BatchSize = 2;
            sqlbulk.WriteToServer(tblDetails);
            transaction.Commit();
        
        catch (Exception exp)
        

            transaction.Rollback();
        
        finally
        
            transaction.Dispose();
            connection.Close();
        

即批量插入。 您可以将其插入临时表。 然后你可以执行SP,它会在服务器端创建必要的索引并更新必要的数据

看here

【讨论】:

【参考方案3】:

尝试使用 linq 查询进行批量更新。这将提高应用程序的性能。

您可以在此处获得有关大规模更新 linq 查询的帮助:

How to run a mass update/delete query in Linq?

http://msdn.microsoft.com/en-us/library/bb399339.aspx

希望这会对你有所帮助。

【讨论】:

不!对于批量操作,Linq 比 ADO.Net 慢(有时非常慢)。 @EricJ。是吗?我认为linq更快。我还使用 Linq 进行 25000 到 30000 条记录的插入、更新删除。我观察到,它使每秒 20 条记录的操作比 ADO.NET 更快 @Freelancer 是的,对 SQL 数据的批量操作可以比同等的 Linq 快得多。 Linq 插入/更新是逐条记录的,批量操作可以在一次操作中插入/更新数千条记录。 @Freelancer:您是指 Linq-to-SQL 还是 Linq-to-Entities (EF)?我没有使用过 Linq-to-SQL,但在我的用例中,Linq-to-Entities 的速度较慢。谷歌实体框架慢,你会看到很多关于这个问题的帖子。 @EricJ.: EF 在几乎所有种情况下都比较慢

以上是关于批量更新 SQL 服务器的 C# 代码的主要内容,如果未能解决你的问题,请参考以下文章

c# winform 如何批量更新数据库数据

sql server有批量插入和批量更新的sql语句吗

sql语句将Excel中的一列批量更新到sql server中的一列中?

sql在update更新时如何快速且大批量的更新数据

mysql批量更新出错

C# 中的批量更新