15

私は、DataTable を通過する「WriteToServer」メソッドを呼び出すことができる C# SqlBulkCopy クラスに精通しています。

私の質問は、そのデータを一括挿入するために使用される SQL サーバーの基になるメカニズムは何ですか?

私が尋ねる理由は、一括挿入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. どのexternがSNIWriteAsyncWrapper () またはSNIWriteSyncOverAsync ( ) を呼び出すか

ここが難しいところです。これは次のとおりだと思いますが、そこにたどり着いた方法は少しハックです。sni.dll のコピーのファイル プロパティを開き、[詳細] タブに移動すると、製品バージョン プロパティ内に d0d5c7b49271cadb6d97de26d8e623e98abdc8db の「コミット ハッシュ」への参照が見つかりました。

だから私はそのハッシュをグーグルで検索し、このNuget検索を介して、タイトルに「System.Data.SqlClient.sni」が含まれるこのNugetパッケージを見つけました。これは名前空間System.Data.SqlClient.SNI意味します適切な方法がなく、実際にはサーバーと通信していないようです。

ここで私はノウハウを使い果たしました。これは、どこにも見つからないネイティブコードに入る前に、私が得ることができるほど深いものです。そして、上にある他のすべてのノイズが何であったかはわかりませんが...

  1. ステップ 4 ( WriteToServerInternalRestContinuedAsync ()) もAnalyzeTargetAndCreateUpdateBulkCommand ()を呼び出すことを思い出してください。
  2. updateBulkCommandText という名前の StringBuilder 内で SQL クエリを連結します。その最後のリンクの 544 行目。

TLDR: 最終的には、INSERT BULKクエリ (ファイルを必要としない) を実行するだけで、実際にはBULK INSERTを使用していないようです (ファイルを必要としません)。これら 2 つのコマンドは非常によく似ていることに注意してください。

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 に答える