如何使用 C#/SQL 批量更新 1000 条记录

Posted

技术标签:

【中文标题】如何使用 C#/SQL 批量更新 1000 条记录【英文标题】:How to bulk update 1000 records using C#/SQL 【发布时间】:2021-11-21 20:50:36 【问题描述】:

我正在从包含一百万条记录的表中读取数据,我对某些列进行了一些修改,我想将它们批量保存。我想在每 10000 条记录后执行一次批量更新。

我正在使用 .Net Core 3.1

我的代码:

public void FixText()
        
            SqlConnection cnn = new SqlConnection(strConn);
            string queryString = "SELECT ClientID, ServerName, Text FROM  [dbo].[Client]";
            SqlCommand selectCmd = new SqlCommand(queryString, cnn);
            cnn.Open();
            int j = 0;
            SqlDataReader reader = selectCmd.ExecuteReader();
            List<Client> clients = new List<Client>();
            try
            
                while (reader.Read())
                
                    j++;
                    ClientID = (int)reader["ClientID"];
                    ServerName = reader["ServerName"].ToString();
                    Text = reader["Text"].ToString();
                    
                    
                    //Modify Text & set ServerName
                    string Text = UpdateText(Text);

                    if text.StartWith("doo")ServerName = "re";

                    //perform bulk update
                    if (j > 10000)
                    
                        Client client = new Client()
                        
                            ClientID = (int)ClientID,
                            ServerName = ServerName,
                            Text = Text,
                        ;
                        clients.Add(client);
                        //I'm struggling here on what to use to do a bulk update
                       
                        j = 0;
                    
                

            
            catch (Exception ex)
            
                throw ex;
            
            finally
            
                reader.Close();
            
            cnn.Close();
        

任何帮助表示赞赏!

【问题讨论】:

你能完全在 SQL 中完成它们,以避免任何 C# 吗? 很遗憾我做不到,有很多数据超出了 SQL 范围。 90% 的时间最快的解决方案是将 SqlBulkCopy 到一个临时/临时表,然后通过 JOIN 对原始表运行更新。 我被你的 if(j>10000) 条件弄糊涂了……你不应该每次都添加一个新客户吗?如果 j>10000 则保存?还有什么理由您不使用实体框架来进行数据操作?如果您想坚持使用此解决方案,我会阅读所有内容,然后打开一个新连接,遍历您的记录并根据您的数据创建更新语句。每 10k 条记录运行更新命令。 在这里查看。google.com/url?sa=t&source=web&rct=j&url=https://… 【参考方案1】:

您有两个选择,要么使用MERGE 语句,要么使用UPDATE

我会选择UPDATE 选项,因为它是最简单的选项。 (这需要FastMember nuget)。

private void ExecuteSql(SqlConnection connection , string sql , SqlParameter[] parameters = null)

    if(connection == null)
    
        throw new ArgumentNullException(nameof(connection));
    

    if(string.IsNullOrWhiteSpace(sql))
    
        throw new ArgumentNullException(nameof(sql));
    
    
    using(var command = new SqlCommand(sql , connection))
    
        if(parameters?.Length > 0)
        
            command.Parameters.AddRange(parameters);
        

        if(connection.State != ConnectionState.Open)
            connection.Open();

        command.ExecuteNonQuery();
    


private void ExecuteBulkCopy<T>(SqlConnection connection , IEnumerable<T> entries , string destinationTableName , string[] columns = null , int batchSize = 1000000)

    if(connection == null)
    
        throw new ArgumentNullException(nameof(connection));
    

    if(entries == null)
    
        throw new ArgumentNullException(nameof(entries));
    

    if(string.IsNullOrWhiteSpace(destinationTableName))
    
        throw new ArgumentNullException(nameof(destinationTableName));
    

    if(connection.State != ConnectionState.Open)
        connection.Open();

    using(SqlBulkCopy sbc = new SqlBulkCopy(connection)
    
        BulkCopyTimeout = 0 ,
        DestinationTableName = destinationTableName ,
        BatchSize = batchSize
    )
    
        using(var reader = ObjectReader.Create(entries , columns))
        
            sbc.WriteToServer(reader);
        
    


private IEnumerable<Client> GetUpdatedClients(SqlConnection connection)

    using(var command = new SqlCommand("SELECT ClientID, ServerName, Text FROM  [dbo].[Client]", connection))
    
        connection.Open();
        using(SqlDataReader reader = _connection.ExecuteReader(query , parameters))
        
            if(reader.HasRows)
            
                while(reader.Read())
                
                    if(reader.IsDBNull(x))  continue; 

                    var clientId   = (int)reader["ClientID"]; 
                    var serverName = reader["ServerName"]?.ToString(); 
                    var text = reader["Text"]?.ToString();
                    
                    //Modify Text & set ServerName
                    string textUpdated = UpdateText(text);                  
                    
                    if(textUpdated.StartWith("doo"))
                    
                        serverName = "re";
                    
                    
                    var client = new Client()
                    
                        ClientID = clientId,
                        ServerName = serverName,
                        Text = textUpdated
                    ;
                    
                    yield return client;
                    
                
            
                               
       


private void BulkUpdateClients(SqlConnection connection, IEnumerable<Client> clients)

    const string dropTempTable = "IF OBJECT_ID('[tempdb].[dbo].[##Client]') IS NOT NULL DROP TABLE [tempdb].[dbo].[##Client];";
    
    // drop temp table if exists
    ExecuteSql(connection ,dropTempTable);
    
    // create the temp table
    ExecuteSql($"SELECT TOP 1 [ClientID], [ServerName], [Text] INTO [tempdb].[dbo].[##Client] FROM [dbo].[Client];");

    // copy rows to the temp table
    ExecuteBulkCopy(connection, clients , "[tempdb].[dbo].[##Client]", new[]  "ClientID", "ServerName", "Text" );

    // Use UPDATE JOIN
    ExecuteSql("UPDATE t1 SET [ServerName] = t2.[ServerName], [Text] = t2.[Text] FROM [dbo].[Client] t1 JOIN [tempdb].[dbo].[##Client] t2 ON t1.[ClientID] = t2.[ClientID];");

    // drop temp table
    ExecuteSql(connection,dropTempTable);
    


public void BulkUpdateClients()

    try
    
        using(var connection = new SqlConnection(strConn))
        
            connection.Open();
            
            var clients = GetUpdatedClients(connection);
            
            // it's important to use the same connection and keep it a live
            // otherwise the temp table will be dropped.
            BulkUpdate(connection, clients);                
        
        
    
    catch(Exception ex)
    
        throw ex;
    

如果您不需要使用临时表,可以将其更改为永久表(只需更改临时表名称即可)。

【讨论】:

这行得通!谢谢!【参考方案2】:

解决这个问题有两种方法。 您可以使用User-Defined TypesSending array of record as JSON datatype through store procedure

UDT

OPENJSON

【讨论】:

您有 UDT 的明确示例吗?

以上是关于如何使用 C#/SQL 批量更新 1000 条记录的主要内容,如果未能解决你的问题,请参考以下文章

SQL查询更新1000条记录

sql在update更新时如何快速且大批量的更新数据(C#中写的)

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

批量向MySQL导入1000万条数据的优化

实体框架的大批量更新比我自己批量更新慢得多

JDBC 批量插入异常处理以了解特定的失败记录