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

时间:2012-05-01 22:39:40

标签: c# sql sql-server

我有一个DataTable,需要把整个东西推到数据库表中。

我可以使用foreach并一次插入每一行来获取所有内容。虽然因为有几千行,但这种情况非常缓慢。

有没有办法一次完成整个数据表可能会更快?

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

6 个答案:

答案 0 :(得分:58)

我发现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);
}

答案 1 :(得分:31)

由于您已经拥有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();
}

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

答案 2 :(得分:3)

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

using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
    bulkCopy.DestinationTableName = 
        "dbo.BulkCopyDemoMatchingColumns";

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

答案 3 :(得分:1)

如果可以偏离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。缺少的属性将保留为其默认值(int为0,引用类型为null等。)

2)将对象推入数据库

一种方法是使用EntityFramework.BulkInsert扩展名。但是,需要EF datacontext。

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

虽然不是直接的方法,但它有助于构建一个使用对象列表而不是DataTable的{​​{1}}的基础。

答案 4 :(得分:0)

您可以使用表值参数执行此操作。

查看以下文章:

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

答案 5 :(得分:0)

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

  

步骤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.修改创建脚本然后执行。