親/子関係を持つSQLの一括挿入は、順序が保持されますか?

c# sqlbulkcopy sql-server-2008

質問

以下に述べるこれらの他の質問と同様に、私は構造を持つ2つのテーブルを持っています:

create table parent (
   recno int identity(1,1) primary key not null,
   groupCode int,
   parentdata varchar(80)
);

create table child (
   parentrecno int not null,
   childdata varchar(80)
)

私はこれらのテーブルに数十万レコードをすばやく挿入する必要があります。このテーブルには、この挿入に無関係の何百万ものレコードが保存されています。親子関係の性質のため、 SqlBulkCopyにとっては良い候補ではありません。

C#でSqlCommandINSERT使用SqlCommandと、400-500レコード/秒が挿入されてしまいます。これは少し遅いです。擬似コード:

 foreach(Record r in parentRecords)
 {
      Insert Fields from r into SqlCommand Parameters but not "recno"
      Call ExecuteScalar to insert and fetch the inserted identity value (recno)
      foreach(ChildRecord cr in parentRecords.Children)
      {
          Insert Fields from cr into SqlCommand Parameters
          Insert the identity value (recno) from above into Parameters 
                                                       (as parentrecno)
          Call ExecuteNonQuery to insert the record
      }   
 }

それらの他の投稿を読んだ後、私にも起こりました。親レコードに添付されたgroupCodeは、挿入する親レコードのセットに固有です。それはうまくいく:

  1. SqlBulkCopyで親レコードを一括して挿入し、通常どおりrecno IDフィールドを自動的に生成させます。
  2. 挿入されたレコードだけでSELECTを実行します。

    select recno from parent where groupCode = @thisgroup order by recno;
    
  3. 取得した値を使用して、メモリ内の子レコードのparentrecnoレコードフィールドを入力します

  4. SqlBulkCopyて子レコードを一括挿入する

これは、元のDataTableと同じ順序でSQLテーブルに入る親レコード(および同じ順序で割り当てられるID値)に依存します。 私はこれに頼ることができるのでしょうか?

関連する質問:

自動生成されたアイデンティティキーを使用してデータセットの親テーブルと子テーブルを更新する方法

ID列の親子関係を持つSqlBulkCopyとDataTables

受け入れられた回答

ターゲット表と同じ構造を持つ2つのステージング表を作成しますが、recno列ではIDを使用しないでください。

create table parentTmp (
   recno int,
   groupCode int,
   parentdata varchar(80)
);

create table childTmp (
   parentrecno int not null,
   childdata varchar(80)
)

recno / parentrecnoの値をそのままにして、ステージング表にデータをバルク・ロードします。

次に、 マージ出力を使用てステージング表からデータを移動できます。

-- Table variable to hold mapping between 
-- SourceRecno and TargetRecno
declare @recno table(SourceRecno int, TargetRecno int);

-- Merge data from parentTmp to parent
-- Output old and new recno to @recno
merge parent T
using parentTmp S
on 0=1
when not matched then
  insert (groupCode, parentdata)
    values (S.groupCode, S.parentData)
output S.recno, inserted.recno into @recno;

-- Copy data from childTmp to child
-- Use @recno to get the new recno
insert into child(parentrecno, childdata)
select R.TargetRecno, C.childdata
from childTmp as C
  inner join @recno as R
    on C.parentrecno = R.SourceRecno;

これはSQL Server 2008でのみ動作します(後で私は推測します)。


人気のある回答

絶対一括挿入ではなく、親データと同時にすべての子データを挿入するため、DBへのラウンドトリップはわずか1回になります。

insert into parent(groupcode, parentdata) values(1, 'parent data');
insert into child(parentrecno, childdata) select parentrecno, childdata from (
    select SCOPE_IDENTITY() as parentrecno, 'child data 1' as childdata
    union
    select SCOPE_IDENTITY() as parentrecno, 'child data 2' as childdata
    union
    select SCOPE_IDENTITY() as parentrecno, 'child data 3' as childdata
) childrendata;

このようなスクリプトをC#コードで作成し、親ごとに1つのリクエストを実行することができます。

子データの量が大きいことがわかっている場合、これは適切な方法ではない可能性があることに注意してください。詳細はわかりませんが、SQLスクリプトのサイズは無期限に拡大できません。



ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ