一次将整个 DataTable 插入数据库而不是逐行插入?

Posted

技术标签:

【中文标题】一次将整个 DataTable 插入数据库而不是逐行插入?【英文标题】:Insert entire DataTable into database at once instead of row by row? 【发布时间】:2012-05-11 10:23:30 【问题描述】:

我有一个 DataTable,需要将整个内容推送到数据库表中。

我可以用一个 foreach 把它全部放在那里,一次插入每一行。由于有几千行,这会非常缓慢。

有没有什么方法可以更快地一次性完成整个数据表?

DataTable 的列数少于 SQL 表。其余的应为空。

【问题讨论】:

SqlBulkCopy 绝对是一条路 codedigest.com/Articles/Framework/… 用于 SqlBulkCopy 示例。 【参考方案1】:

我发现 SqlBulkCopy 是一种简单的方法,并且不需要在 SQL Server 中编写存储过程。

这是我如何实现它的示例:

// take note of SqlBulkCopyOptions.KeepIdentity , you may or may not want to use this for your situation.  

using (var bulkCopy = new SqlBulkCopy(_connection.ConnectionString, SqlBulkCopyOptions.KeepIdentity))

      // my DataTable column names match my SQL Column names, so I simply made this loop. However if your column names don't match, just pass in which datatable name matches the SQL column name in Column Mappings
      foreach (DataColumn col in table.Columns)
      
          bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
      

      bulkCopy.BulkCopyTimeout = 600;
      bulkCopy.DestinationTableName = destinationTableName;
      bulkCopy.WriteToServer(table);

【讨论】:

【参考方案2】:

由于您已经有一个 DataTable,并且我假设您使用的是 SQL Server 2008 或更高版本,因此这可能是最直接的方法。首先,在您的数据库中,创建以下两个对象:

CREATE TYPE dbo.MyDataTable -- you can be more speciifc here
AS TABLE
(
  col1 INT,
  col2 DATETIME
  -- etc etc. The columns you have in your data table.
);
GO

CREATE PROCEDURE dbo.InsertMyDataTable
  @dt AS dbo.MyDataTable READONLY
AS
BEGIN
  SET NOCOUNT ON;

  INSERT dbo.RealTable(column list) SELECT column list FROM @dt;
END
GO

现在在您的 C# 代码中:

DataTable tvp = new DataTable();
// define / populate DataTable

using (connectionObject)

    SqlCommand cmd = new SqlCommand("dbo.InsertMyDataTable", connectionObject);
    cmd.CommandType = CommandType.StoredProcedure;
    SqlParameter tvparam = cmd.Parameters.AddWithValue("@dt", tvp);
    tvparam.SqlDbType = SqlDbType.Structured;
    cmd.ExecuteNonQuery();

如果您在问题中提供了更具体的细节,我会给出更具体的答案。

【讨论】:

如果我没记错的话,这仅适用于相对少量的数据,因为@dt 将逐行填充。 SqlBulkCopy 对于大数据(数千甚至更多)的效率要高得多。 @Aaron Bertrand 我为 3 个不同的表插入了超过 600000 条记录。表 A 不依赖于任何其他表,而表 B 需要表 A 的主键,表 C 需要 A 和 B 的主键值。表 D 也需要更新 10000 条记录。我怎样才能做到这一点。 在插入过程中我可以检查并从数据表标题创建一个表【参考方案3】:

考虑this approach,你不需要for循环:

using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))

    bulkCopy.DestinationTableName = 
        "dbo.BulkCopyDemoMatchingColumns";

    try
    
        // Write from the source to the destination.
        bulkCopy.WriteToServer(ExistingSqlTableName);
    
    catch (Exception ex)
    
        Console.WriteLine(ex.Message);
    

【讨论】:

对于像我这样的新手:bulkCopy.WriteToServer(ExitingSqlTableName);这里的 ExistingSqlTableName 表示现有的 sql 数据源。它可以是 sql 表、数据表或阅读器。 这应该是公认的答案。谢谢。【参考方案4】:

如果可以稍微偏离DataTable -> SQL表的直线路径,也可以通过对象列表来完成:

1) DataTable -> 通用对象列表

public static DataTable ConvertTo<T>(IList<T> list)

    DataTable table = CreateTable<T>();
    Type entityType = typeof(T);
    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entityType);

    foreach (T item in list)
    
        DataRow row = table.NewRow();

        foreach (PropertyDescriptor prop in properties)
        
            row[prop.Name] = prop.GetValue(item);
        

        table.Rows.Add(row);
    

    return table;

来源和更多细节可以在here找到。缺少的属性将保持其默认值(ints 为 0,引用类型为 null 等)

2) 将对象推送到数据库中

一种方法是使用EntityFramework.BulkInsert 扩展名。不过,需要 EF 数据上下文。

它生成快速插入所需的 BULK INSERT 命令(用户定义的表类型解决方案比这慢得多)。

虽然不是直接的方法,但它有助于构建使用对象列表的基础,而不是 DataTables 和 seems to be much more memory efficient。

【讨论】:

【参考方案5】:

我更喜欢用户定义的数据类型:它超级快。

第 1 步:在 Sql Server DB 中创建用户定义表

CREATE TYPE [dbo].[udtProduct] AS TABLE(
  [ProductID] [int] NULL,
  [ProductName] [varchar](50) NULL,
  [ProductCode] [varchar](10) NULL
)
GO

第 2 步:创建具有用户定义类型的存储过程

CREATE PROCEDURE ProductBulkInsertion 
@product udtProduct readonly
AS
BEGIN
    INSERT INTO Product
    (ProductID,ProductName,ProductCode)
    SELECT ProductID,ProductName,ProductCode
    FROM @product
END

第 3 步:从 c# 执行存储过程

SqlCommand sqlcmd = new SqlCommand("ProductBulkInsertion", sqlcon);
sqlcmd.CommandType = CommandType.StoredProcedure;
sqlcmd.Parameters.AddWithValue("@product", productTable);
sqlcmd.ExecuteNonQuery();

可能的问题:更改用户定义的表

其实并没有sql server命令来改变用户定义的类型 但在管理工作室中,您可以通过以下步骤实现这一目标

1.为该类型生成脚本。(在新的查询窗口中或作为文件) 2.删除用户定义表。 3.修改创建脚本,然后执行。

【讨论】:

【参考方案6】:

您可以使用表值参数来做到这一点。

看看下面的文章:

http://www.codeproject.com/Articles/39161/C-and-Table-Value-Parameters

【讨论】:

以上是关于一次将整个 DataTable 插入数据库而不是逐行插入?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 VBA 将“整个”DAO 记录集插入到表中

Coredata 总是更新行而不是快速插入新行

一次将datagridview的所有数据插入数据库

SELECT FOR UPDATE 在 MySQL 中保存整个表,而不是逐行保存

DataTable update() 插入重复的新行而不检查它是不是存在

在 Python 中:一次将 QtableWidget 中的所有条目插入数据库表中