我有一个大约3000行的数据表。每个行都需要插入数据库表中。目前,我正在运行一个foreach循环,如下所示:
obj_AseCommand.CommandText = sql_proc;
obj_AseCommand.CommandType = CommandType.StoredProcedure;
obj_AseCommand.Connection = db_Conn;
obj_AseCommand.Connection.Open();
foreach (DataRow dr in dt.Rows)
{
obj_AseCommand.Parameters.AddWithValue("@a", dr["a"]);
obj_AseCommand.Parameters.AddWithValue("@b", dr["b"]);
obj_AseCommand.Parameters.AddWithValue("@c", dr["c"]);
obj_AseCommand.ExecuteNonQuery();
obj_AseCommand.Parameters.Clear();
}
obj_AseCommand.Connection.Close();
您能告诉我如何在数据库中并行执行SP,因为上述方法需要大约10分钟才能插入3000行。
答案 0 :(得分:9)
修改强>
事后看来,使用Parallel.ForEach
来并行化数据库插入是有点浪费的,因为它也会为每个连接消耗一个线程。可以说,更好的并行解决方案是使用System.Data
Db操作的异步版本,例如ExecuteNonQueryAsync ,启动执行(并发),然后使用await Task.WhenAll()
等待完成 - 这将避免调用者的线程开销,尽管整体Db性能可能不会更快。 More here
原始答案,多个并行插入数据库
您可以使用TPL并行执行此操作,例如:特别是localInit
超载Parallel.ForEach。您几乎肯定希望通过调整MaxDegreeOfParalelism来限制并行数量,以免您淹没数据库:
Parallel.ForEach(dt.Rows,
// Adjust this for optimum throughput vs minimal impact to your other DB users
new ParallelOptions { MaxDegreeOfParallelism = 4 },
() =>
{
var con = new SqlConnection();
var cmd = con.CreateCommand();
cmd.CommandText = sql_proc;
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
cmd.Parameters.Add(new SqlParameter("@a", SqlDbType.Int));
// NB : Size sensitive parameters must have size
cmd.Parameters.Add(new SqlParameter("@b", SqlDbType.VarChar, 100));
cmd.Parameters.Add(new SqlParameter("@c", SqlDbType.Bit));
// Prepare won't help with SPROCs but can improve plan caching for adhoc sql
// cmd.Prepare();
return new {Conn = con, Cmd = cmd};
},
(dr, pls, localInit) =>
{
localInit.Cmd.Parameters["@a"] = dr["a"];
localInit.Cmd.Parameters["@b"] = dr["b"];
localInit.Cmd.Parameters["@c"] = dr["c"];
localInit.Cmd.ExecuteNonQuery();
return localInit;
},
(localInit) =>
{
localInit.Cmd.Dispose();
localInit.Conn.Dispose();
});
注意:
.Prepare()
DataTable's
行是线程安全的。当然,你要仔细检查一下。旁注:
即使使用宽表和单线程,3000行的10分钟也是过多的。你的过程做了什么?我假设处理不是微不足道的,因此需要SPROC,但是如果你只是做简单的插入,按照@ 3dd的评论,SqlBulkCopy将合理地每分钟插入大约1M行狭窄的桌子。
答案 1 :(得分:4)
最好将整个数据表传递到数据库
obj_AseCommand.CommandText = sql_proc;
obj_AseCommand.CommandType = CommandType.StoredProcedure;
obj_AseCommand.Connection = db_Conn;
obj_AseCommand.Connection.Open();
obj_AseCommand.Parameters.AddWithValue("@Parametername",DataTable);
obj_AseCommand.ExecuteNonQuery();
在数据库中,您必须创建与您的数据表完全匹配的表类型
CREATE TYPE EmpType AS TABLE
(
ID INT, Name VARCHAR(3000), Address VARCHAR(8000), Operation SMALLINT //your columns
)
在商店程序中你可以做这样的事情......
create PROCEDURE demo
@Details EmpType READONLY // it must be read only
AS
BEGIN
insert into yourtable //insert data
select * from @Details
END
答案 2 :(得分:3)
您可以使用SqlBulkCopy
。请参阅下面的示例代码WriteToServer
方法将datatable
写入数据库,前提是它们具有相同的映射
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(ConSQL)) {
if (ConSQL.State == ConnectionState.Closed) {
ConSQL.Open();
}
bulkCopy.ColumnMappings.Add(0, 0);
bulkCopy.ColumnMappings.Add(1, 1);
bulkCopy.ColumnMappings.Add(2, 2);
bulkCopy.DestinationTableName = "dbo.TableName";
bulkCopy.WriteToServer(dataTable);
bulkCopy.Close(); //redundant - since using will dispose the object
}
答案 3 :(得分:0)
您可以使用SqlBulkCopy
指南是here