web-dev-qa-db-ja.com

SQLBulkCopy "存在する場合は挿入または更新"する方法はありますか?

非常に大きなテーブルを定期的に更新する必要がありますが、SQLBulkCopyはそのために最適です。重複を防ぐ2列のインデックスがあるだけです。 SQLBulkCopyを「存在する場合は挿入または更新」として使用する方法はありますか?

そうでない場合、そうするための最も効率的な方法は何ですか?繰り返しになりますが、何百万ものレコードを含むテーブルについて話しています。

ありがとうございました

22
Sol

一時的なステージングテーブルにデータを一括で読み込み、次にファイナルテーブルにアップサートします。アップサートの例については、 http://www.databasejournal.com/features/mssql/article.php/3739131/UPSERT-Functionality-in-SQL-Server-2008.htm を参照してください。

10
btilly

この問題を解決するために、nugetパッケージ(SqlBulkTools)を公開しました。

一括アップサートを実行するコード例を次に示します。

var bulk = new BulkOperations();
var books = GetBooks();

using (TransactionScope trans = new TransactionScope())
{
    using (SqlConnection conn = new SqlConnection(ConfigurationManager
    .ConnectionStrings["SqlBulkToolsTest"].ConnectionString))
    {
        bulk.Setup<Book>()
            .ForCollection(books)
            .WithTable("Books")
            .AddAllColumns()
            .BulkInsertOrUpdate()
            .MatchTargetOn(x => x.ISBN)
            .Commit(conn);
    }

    trans.Complete();
}

非常に大きなテーブルの場合、テーブルロックを追加し、非クラスター化インデックスを一時的に無効にするオプションがあります。その他の例については、 SqlBulkToolsのドキュメント を参照してください。

12
Greg R Taylor

1つのステップではなく、SQL Server2008では、次のことができます。

  • ステージングテーブルへの一括読み込み
  • MERGEステートメントを適用して、実際のテーブルに更新/挿入します

MERGEステートメントの詳細を読む

11
marc_s

新しい一時テーブルを作成する代わりに、BTWがより多くのスペースとメモリを消費します。

INSTEAD OF INSERTを使用してトリガーを作成し、MERGEステートメント内で使用しました。

ただし、SqlBulkCopyにパラメーターSqlBulkCopyOptions.FireTriggersを追加することを忘れないでください。

これは私の2セントです。

5
Ivan Paniagua

別の代替方法は、一時テーブルを使用せずに、テーブル値パラメーターを持つストアード・プロシージャーを使用することです。データテーブルをspに渡し、そこでマージを行います。

1
jb5253

@Ivanからヒントを得ました。必要になるかもしれない人のために、これが私がやったことです。

create trigger yourschma.Tr_your_triger_name
    on yourschma.yourtable
    instead of INSERT
    as
    merge into yourschma.yourtable as target
    using inserted as source
    on (target.yourtableID = source.yourtableID)
    when matched then
        update
        set target.ID     = source.ID,
            target.some_column = source.some_column,
            target.Amount                       = source.Amount
    when not matched by target then
        insert (some_column, Amount)
        values (source.some_column, source.Amount);
go
0
Shengfeng Li