非常に大きなテーブルを定期的に更新する必要がありますが、SQLBulkCopyはそのために最適です。重複を防ぐ2列のインデックスがあるだけです。 SQLBulkCopyを「存在する場合は挿入または更新」として使用する方法はありますか?
そうでない場合、そうするための最も効率的な方法は何ですか?繰り返しになりますが、何百万ものレコードを含むテーブルについて話しています。
ありがとうございました
一時的なステージングテーブルにデータを一括で読み込み、次にファイナルテーブルにアップサートします。アップサートの例については、 http://www.databasejournal.com/features/mssql/article.php/3739131/UPSERT-Functionality-in-SQL-Server-2008.htm を参照してください。
この問題を解決するために、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のドキュメント を参照してください。
1つのステップではなく、SQL Server2008では、次のことができます。
MERGE
ステートメントを適用して、実際のテーブルに更新/挿入します新しい一時テーブルを作成する代わりに、BTWがより多くのスペースとメモリを消費します。
INSTEAD OF INSERTを使用してトリガーを作成し、MERGEステートメント内で使用しました。
ただし、SqlBulkCopyにパラメーターSqlBulkCopyOptions.FireTriggersを追加することを忘れないでください。
これは私の2セントです。
別の代替方法は、一時テーブルを使用せずに、テーブル値パラメーターを持つストアード・プロシージャーを使用することです。データテーブルをspに渡し、そこでマージを行います。
@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