我有一个 DataTable,需要将整个东西推送到一个数据库表中。
我可以用一个 foreach 把它全部放在那里,一次插入每一行。由于有几千行,这很慢。
有什么方法可以一次完成整个数据表可能更快?
DataTable 的列数少于 SQL 表。其余的应该留空。
我有一个 DataTable,需要将整个东西推送到一个数据库表中。
我可以用一个 foreach 把它全部放在那里,一次插入每一行。由于有几千行,这很慢。
有什么方法可以一次完成整个数据表可能更快?
DataTable 的列数少于 SQL 表。其余的应该留空。
我发现 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);
}
由于您已经有一个 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();
}
如果您在问题中提供了更具体的细节,我会给出更具体的答案。
考虑这种方法,您不需要 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);
}
}
如果可以稍微偏离 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;
}
来源和更多细节可以在这里找到。缺少的属性将保持其默认值(int
s 为 0,引用类型为 null 等)
2)将对象推送到数据库中
一种方法是使用EntityFramework.BulkInsert
扩展。但是,需要 EF 数据上下文。
它生成快速插入所需的 BULK INSERT 命令(用户定义的表类型解决方案比这慢得多)。
虽然不是直接的方法,但它有助于构建使用对象列表而不是DataTable
s 的基础,这似乎更节省内存。
我更喜欢用户定义的数据类型:它超级快。
第 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.修改创建脚本,然后执行。