15

我熟悉 C# SqlBulkCopy 类,您可以在其中调用通过 DataTable 传递的“WriteToServer”方法。

我的问题是 SQL Server 中的什么底层机制用于批量插入该数据?

我问的原因是批量插入MSDN T-SQL 帮助文件中引用的批量插入需要导入数据文件。SqlBulkCopy 是否创建数据文件?

我想了解这些东西以确定我是否可以在 SQL 中使用批量插入功能。

如果我编写一个 SQL 语句,准备将所有行插入到特定表(数千行)中,我可以将它们批量插入到目标表中吗?像这样的事情就是我现在正在做的事情,

INSERT INTO sync_filters (table_name, device_id, road_id, contract_id)
    SELECT * FROM dbo.sync_contract_filters (@device_id)

dbo.sync_contract_filters 是一个生成所有要插入的行的函数。这个可以批量插入吗?

4

3 回答 3

14

SqlBulkCopy 不创建数据文件。它使用可用的通信协议(命名管道、TCP/IP 等)将数据表直接从 .Net DataTable 对象流式传输到服务器,并使用 BCP 使用的相同技术将数据批量插入目标表.

于 2012-08-30T03:07:20.177 回答
9

花了7年,但我们终于有了答案……

根据 Sam Anwar 的回答,我可以确认它正在将数据转换为原始字节流并将其写入 SQL,就好像它是从文件中流入一样。它如何欺骗 SQL 使其认为它正在读取文件超出了我的理解。

我想从查询内部进行批量插入,以加快慢速聚集索引插入。在这里找到你的帖子后,不知何故,我对它产生了令人不安的兴趣,所以我花了几个小时研究它。

实际向服务器写入数据的执行路径似乎是:

您的代码:

  1. 您的代码调用 System.Data.SqlClient.SqlBulkCopy.WriteToServer()

System.Data.SqlClient.SqlBulkCopy 内部:

  1. 它调用WriteRowSourceToServerAsync ()
  2. 它调用WriteRowSourceToServerCommon () 来映射列并调用WriteToServerInternalAsync () 来写入数据
  3. 它调用WriteToServerInternalRestContinuedAsync ()
  4. 它调用了 AnalyzeTargetAndCreateUpdateBulkCommand () (这就是答案。跳到第 14 步来阅读它。)CopyBatchesAsync ()
  5. 其中 (CopyBatchesAsync) 调用SubmitBulkUpdateCommand ()

-- 在 System.Data.SqlClient.TdsParser 内部:

  1. 它调用 System.Data.SqlClient.TdsParser。TdsExecuteSQLBatch ()
  2. 它调用WriteString () 或类似方法将数据转换为字节数组
  3. 它调用WriteByteArray ()
  4. 它调用WritePacket ()
  5. 它调用WriteSni ()
  6. 调用SNIWritePacket ()

-- 在 System.Data.SqlClient.SNINativeMethodWrapper 内部:

  1. 它调用System.Data.SqlClient.SNINativeMethodWrapper.SNIWritePacket ()
  2. 哪个外部调用SNIWriteAsyncWrapper () 或SNIWriteSyncOverAsync ()

现在这是棘手的地方。我认为这如下,但我是如何到达那里的有点hacky。我在我的 sni.dll 副本上打开了文件属性,转到详细信息选项卡,在 Product Version 属性中,我找到了对 d0d5c7b49271cadb6d97de26d8e623e98abdc8db 的“提交哈希”的引用。

所以我用谷歌搜索了那个哈希,通过这个 Nuget 搜索我找到了这个 Nuget 包,它的标题包括“System.Data.SqlClient.sni”,这意味着我在这里找到的命名空间System.Data.SqlClient.SNI ,但这并没有'没有正确的方法,实际上似乎没有与服务器通信。

所以这就是我用尽专业知识的地方;在它进入我在任何地方都找不到的本机代码之前,这是我所能达到的深度。虽然我不确定上面的所有其他噪音是什么......

  1. 记住第 4 步(WriteToServerInternalRestContinuedAsync ())也调用了 AnalyzeTargetAndCreateUpdateBulkCommand ()
  2. 它在名为 updateBulkCommandText 的 StringBuilder 中连接一个 SQL 查询。最后一个链接中的第 544 行。

TLDR:最终,它似乎只是执行了一个INSERT BULK查询(不需要文件),实际上并没有使用BULK INSERT(确实如此)。请注意,这两个命令看起来非常相似。

Microsoft 文档中的重要说明:

由外部工具用于上传二进制数据流。此选项不适用于 SQL Server Management Studio、SQLCMD、OSQL 等工具或 SQL Server Native Client 等数据访问应用程序编程接口。

我将其解释为“使用风险自负,不要指望帮助”。公平地说,这几乎和绿灯一样好。

于 2020-01-01T05:23:09.920 回答
2

SqlBulkCopy 可以使用数据表、IDataReader 或 DataRow[] 作为数据源。看一下类的 WriteToServer 方法。我发现它是一种将数据导入 SQL Server 的非常有用的机制。我之前已经将它与CSVReader结合使用。上一个链接让您了解它如何与实现 IDataReader 的类一起使用。

您可能需要调整批量大小以确保获得预期的性能。

如果您希望通过快速写入大量数据来获得最佳性能,那么 MSDN 上的数据加载性能指南是一个非常有用的资源。它更多地关注 BCP 和 BULK INSERT 之类的东西,但涉及 SQLBulkCopy 并且还提供了很多值得思考的东西(可能有点太多,但至少无论如何它是一个有用的参考)。

于 2012-08-30T03:34:28.690 回答