我需要定期更新一个非常大的表,SQLBulkCopy非常适合这个任务,只是我有一个由两列组成的索引,防止重复。是否有一种方法可以将SQLBulkCopy用作“插入或更新(如果已存在)”?
如果不行,那么最有效的方法是什么?再次强调,我说的是一个包含数百万条记录的表。
谢谢
我需要定期更新一个非常大的表,SQLBulkCopy非常适合这个任务,只是我有一个由两列组成的索引,防止重复。是否有一种方法可以将SQLBulkCopy用作“插入或更新(如果已存在)”?
如果不行,那么最有效的方法是什么?再次强调,我说的是一个包含数百万条记录的表。
谢谢
我发布了一个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文档。
我会将数据批量加载到临时分段表中,然后执行 upsert 操作到最终表中。请参考这里,了解如何执行 upsert 操作的示例。
我使用了INSTEAD OF INSERT触发器,而不是创建一个新的临时表,这样会消耗更多的空间和内存。
在MERGE语句中使用了该触发器。
但不要忘记在SqlBulkCopy中添加参数SqlBulkCopyOptions.FireTriggers。
这是我的个人经验。
我从@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