首页 文章

C#中的批量更新

提问于
浏览
47

为了在数据库中插入大量数据,我曾经将所有插入信息收集到列表中并将此列表转换为 DataTable . 然后我通过 SqlBulkCopy 将该列表插入数据库 .

我发送生成列表的位置
LiMyList
其中包含我要插入数据库的所有批量数据的信息
并将其传递给我的批量插入操作

InsertData(LiMyList, "MyTable");

InsertData 在哪里

public static void InsertData<T>(List<T> list,string TableName)
        {
                DataTable dt = new DataTable("MyTable");
                clsBulkOperation blk = new clsBulkOperation();
                dt = ConvertToDataTable(list);
                ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
                using (SqlBulkCopy bulkcopy = new SqlBulkCopy(ConfigurationManager.ConnectionStrings["SchoolSoulDataEntitiesForReport"].ConnectionString))
                {
                    bulkcopy.BulkCopyTimeout = 660;
                    bulkcopy.DestinationTableName = TableName;
                    bulkcopy.WriteToServer(dt);
                }
        }    

public static DataTable ConvertToDataTable<T>(IList<T> data)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
            DataTable table = new DataTable();
            foreach (PropertyDescriptor prop in properties)
                table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
            foreach (T item in data)
            {
                DataRow row = table.NewRow();
                foreach (PropertyDescriptor prop in properties)
                    row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                table.Rows.Add(row);
            }
            return table;
        }

现在我想做一个更新操作,是否有任何方法,因为插入数据是由 SqlBulkCopy 完成从C#.Net更新数据到DataBase

7 回答

  • 2

    之前我所做的是从数据执行批量插入到临时表,然后使用命令或存储过程来更新临时表与目标表相关的数据 . 临时表是一个额外的步骤,但与逐行更新数据相比,如果行数很大,则可以通过批量插入和大量更新获得性能提升 .

    例:

    public static void UpdateData<T>(List<T> list,string TableName)
    {
        DataTable dt = new DataTable("MyTable");
        dt = ConvertToDataTable(list);
    
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["SchoolSoulDataEntitiesForReport"].ConnectionString))
        {
            using (SqlCommand command = new SqlCommand("", conn))
            {
                try
                {
                    conn.Open();
    
                    //Creating temp table on database
                    command.CommandText = "CREATE TABLE #TmpTable(...)";
                    command.ExecuteNonQuery();
    
                    //Bulk insert into temp table
                    using (SqlBulkCopy bulkcopy = new SqlBulkCopy(conn))
                    {
                        bulkcopy.BulkCopyTimeout = 660;
                        bulkcopy.DestinationTableName = "#TmpTable";
                        bulkcopy.WriteToServer(dt);
                        bulkcopy.Close();
                    }
    
                    // Updating destination table, and dropping temp table
                    command.CommandTimeout = 300;
                    command.CommandText = "UPDATE T SET ... FROM " + TableName + " T INNER JOIN #TmpTable Temp ON ...; DROP TABLE #TmpTable;";
                    command.ExecuteNonQuery();
                }
                catch (Exception ex)
                {
                    // Handle exception properly
                }
                finally
                {
                    conn.Close();
                }
            }
        }
    }
    

    请注意,单个连接用于执行整个操作,以便能够在每个步骤中使用临时表,因为临时表的范围是每个连接 .

  • 3

    根据我的个人经验,处理这种情况的最佳方法是使用带有 Table-Valued ParameterUser-Defined Table Type 的存储过程 . 只需使用数据表的列设置类型,并将所述数据表作为参数传入SQL命令 .

    在存储过程中,您可以直接连接某个唯一键(如果您要更新的所有行都存在),或者 - 如果您可能遇到必须同时执行更新和插入的情况 - 请在其中使用SQL Merge 命令存储过程以处理适用的更新和插入 .

    微软同时拥有syntax referencearticle with examples .

    对于.NET部分,将参数类型设置为 SqlDbType.Structured 并将所述参数的值设置为包含要更新的记录的数据表是一件简单的事情 .

    该方法提供了清晰度和易维护性的优点 . 虽然可能有一些方法可以提供性能改进(例如将其放入临时表中然后遍历该表),但我认为它们超过让.NET和SQL处理传输表并更新记录本身的简单性 . 吻 .

  • 2

    试试Nuget上可用的SqlBulkTools .

    免责声明:我是这个图书馆的作者 .

    var bulk = new BulkOperations();
    var records = GetRecordsToUpdate();
    
    using (TransactionScope trans = new TransactionScope())
    {
        using (SqlConnection conn = new SqlConnection(ConfigurationManager
        .ConnectionStrings["SqlBulkToolsTest"].ConnectionString))
        {
            bulk.Setup<MyTable>()
                .ForCollection(records)
                .WithTable("MyTable")
                .AddColumn(x => x.SomeColumn1)
                .AddColumn(x => x.SomeColumn2)
                .BulkUpdate()
                .MatchTargetOn(x => x.Identifier)
                .Commit(conn);
        }
    
        trans.Complete();
    }
    

    仅更新'SomeColumn1'和'SomeColumn2' . 更多例子可以找到here

  • 34

    我不确定我是否会要归档...如果您的问题是关于快速替换整个表格内容,那么我会选择 truncatehttp://technet.microsoft.com/en-us/library/ms177570.aspx)并批量插入新的数据部分 . 但是这种方法只有在没有外键约束的情况下才有效 .

    如果你想真正更新,而不是从下面寻找GuillermoGutiérrez的答案 .

  • 1

    我会在临时表中插入新值,然后对目标表进行合并,如下所示:

    MERGE [DestTable] AS D 
    USING #SourceTable S
        ON D.ID = S.ID
    WHEN MATCHED THEN 
        UPDATE SET ...
    WHEN NOT MATCHED 
    THEN INSERT (...) 
    VALUES (...);
    
  • 1

    您可以尝试构建包含所有数据的查询 . 使用 case . 它可能看起来像这样

    update your_table
    set some_column = case when id = 1 then 'value of 1'
                           when id = 5 then 'value of 5'
                           when id = 7 then 'value of 7'
                           when id = 9 then 'value of 9'
                      end
    where id in (1,5,7,9)
    
  • 55

    我会采用TempTable方法,因为这样你就不会锁定任何东西 . 但是如果你的逻辑只需要在前端并且你需要使用批量复制,我会尝试删除/插入方法,但在相同的SqlTransaction中以确保完整性,如下所示:

    // ...
    
    dt = ConvertToDataTable(list);
    
    using (SqlConnection cnx = new SqlConnection(myConnectionString))
    {
        using (SqlTranscation tran = cnx.BeginTransaction())
        {
            DeleteData(cnx, tran, list);
    
            using (SqlBulkCopy bulkcopy = new SqlBulkCopy(cnx, SqlBulkCopyOptions.Default, tran))
            {
                bulkcopy.BulkCopyTimeout = 660;
                bulkcopy.DestinationTableName = TabelName;
                bulkcopy.WriteToServer(dt);
            }
    
            tran.Commit();
        }
    }
    

相关问题