【问题标题】:Fastest way to bulk insert in SQL Table from C# SQLCLR Stored Procedure从 C# SQLCLR 存储过程批量插入 SQL 表的最快方法
【发布时间】:2018-03-25 13:43:13
【问题描述】:

我有一个 C# SQLCLR 存储过程,其中首先将数据填充到某些字典中并进行计算,然后将输出存储在其他一些字典中。

由于使用了字典,此计算完成得非常快,并且完全证明了我需要使用 CLR 存储过程而不是普通的 SQL 存储过程。

但是,我必须从这些输出字典中将数据保存在 SQL 中的某些表中,而这部分需要花费大量时间并且无法满足我对整个 SQLCLR proc 更快的需求。

我必须对每个输出字典的每个键进行迭代,然后必须创建插入查询,然后必须按以下方式运行 ExecuteNonQuery:

那么我该如何改进我的这种方法,以便在插入数据时不会花费时间。我不能将SqlBulkCopy 用作it does not accept the in-process Context Connection(即"Context Connection = true;")作为连接字符串。那么还有其他更快的方法吗?提前致谢。

【问题讨论】:

  • “我不能使用 SQlBulkCopy,因为它不接受 connectionString 作为当前上下文。” - 嗯?你是什​​么意思?你知道这个constructor 吗?我绝对不明白你所说的“connectionString as context”是什么意思。
  • @Corak 我的意思是将我的 CRL proc 的连接设置为“conn = new SqlConnection("context connection=true");”。现在,我不能使用这个语句“使用(SqlBulkCopy bulkCopy = new SqlBulkCopy(conn.ConnectionString))”。
  • 那么使用这个constructor 怎么样? using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn)) { /*...*/}
  • @Corak 不,使用您的第二个解决方案我仍然收到此错误。读取请求的操作在上下文连接上不可用时出错。在 System.Data.SqlClient.SqlBulkCopy.CreateOrValidateConnection(String 方法) 的堆栈跟踪
  • @Corak SqlBulkCopy 的物理限制是目标连接字符串不能使用进程内连接(即上下文连接)。我已经更新了问题的文本以反映正确的术语,希望能减少混淆(稍后将添加支持文档的链接)。

标签: c# sql-server sql-server-2008 import sqlclr


【解决方案1】:

使用SqlBulkCopy Class,或者您可以在sqlc# 中创建自己的User-Defined Table Type - 将您的数据生成为DataTable,而不是某个集合,这将匹配您的用户定义类型,然后将您的DataTable 传递为SqlParameter 并执行。或者您可以将您的 Collection 直接添加到表值参数中,如图所示 HereuseDataTable == false 部分)

当然,为此你应该使用 sql 存储过程。 sql 用户定义表类型的调用部分如下所示:

cmd = new SqlCommand{ Connection = conn, ...
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "sp_YourStoredProcedureName";
cmd.Parameters.Add(new SqlParameter("@UserDefinedTypeParameterName", yourDataTable));
cmd.ExecuteNonQuery();

【讨论】:

  • 这个表格类型的解决方案看起来很有希望。我会检查并告诉你。谢谢老哥!
  • 好的,不客气,随便问问会不会出错。
  • @SeM-ՍեՄ -1 因为第一个选项正是 OP 说他们不能使用的东西,而第二个选项是正确的方向(即使用 TVP)但实施它以一种非常低效的方式,不必要地浪费时间、内存和 CPU。该集合可以按原样流式传输到 TVP 中,而无需将其复制为 aDataSet。请参阅我的答案中的链接,以获取有关如何完成此操作的示例代码。
  • @SolomonRutzky 我看到了你的回答,我不是告诉他将已经初始化的集合写入DataTable,我说不要将它写入某个集合,而是使用DataTable。 (“用你的数据创建一个 DataTable” 而不是“从你的数据”)。
  • 当然,如果他已经有一个集合,他可以将数据流式传输到 TVP,而无需多做一个不必要的步骤。
【解决方案2】:

你有几个选择:

  1. 创建一个用户定义的表类型 (UDTT) 和一个接受该 UDTT 作为参数的 T-SQL 存储过程(即表值参数 (TVP))。由于您已经在集合中拥有数据,绝对不要创建一个单独的DataTable 将数据传输到其中,因为这只是浪费时间、内存和 CPU。您可以通过创建返回IEnumerable<SqlDataRecord>、使用yield return; 并用作代表UDTT 的SqlParameter 的值的方法将其本机集合中的数据直接流式传输到TVP。我在这里有两个这样的例子:

  2. 如果只有 1 个表要填充(因此有 1 个结果集),那么您可以将集合作为结果集从存储过程中输出,并按如下方式使用它:

    INSERT INTO schema.table (column_list)
      EXEC schema.SQLCLR_stored_procedure @param1, ....;
    
  3. 如果只有 1 个表要填充(因此有 1 个结果集),并且如果您没有对违反标量函数规则的上下文连接执行任何操作,那么您可以尝试将 SQLCLR 存储过程更改为 TVF,返回 IEnumerable,并通过 yield return; 遍历返回以将其流式传输。几年前我在 SQL Server Central 上写过一篇文章,其中有一个示例:CLR Table-Valued Function Example with Full Streaming (STVF / TVF)

【讨论】:

  • 谢谢伙计,您在第一点的第二个链接以最好的方式解决了我的目的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-20
相关资源
最近更新 更多