32

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

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

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

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

4

6 回答 6

66

我发现 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);
}
于 2013-11-20T22:55:39.157 回答
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();
}

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

于 2012-05-01T23:08:59.807 回答
5

考虑这种方法,您不需要 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);
    }
}
于 2017-03-04T09:43:33.693 回答
3

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

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;
}

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

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

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

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

虽然不是直接的方法,但它有助于构建使用对象列表而不是DataTables 的基础,这似乎更节省内存

于 2017-01-15T21:13:40.857 回答
1

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

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

于 2016-09-11T14:51:56.080 回答
0

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

看看下面的文章:

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

于 2012-05-01T22:43:07.200 回答