ksc*_*ott 17 c# sql sql-server sqlbulkcopy sql-server-2008
我收到一个包含数千条记录的每日XML文件,每条记录都是我需要存储在内部数据库中用于报告和计费的业务事务.我的印象是每天的文件只包含唯一的记录,但发现我对unique的定义与提供者的定义并不完全相同.
导入此数据的当前应用程序是C#.Net 3.5控制台应用程序,它使用SqlBulkCopy进入MS SQL Server 2008数据库表,其中列与XML记录的结构完全匹配.每个记录只有100多个字段,并且数据中没有自然键,或者更确切地说,我可以想出的字段,因为复合键最终也必须允许空值.目前该表有几个索引,但没有主键.
基本上整行必须是唯一的.如果一个字段不同,则它足以插入.我查看了创建整行的MD5哈希,将其插入数据库并使用约束来阻止SqlBulkCopy插入行,但我不知道如何将MD5哈希进入BulkCopy操作而我不是确定整个操作是否会失败并在任何一个记录失败时回滚,或者它是否会继续.
该文件包含大量记录,在XML中逐行进行,查询数据库以查找与所有字段匹配的记录,然后决定插入实际上是我能够看到能够执行此操作的唯一方法.我只是希望不必完全重写应用程序,并且批量复制操作要快得多.
有没有人知道一种方法来使用SqlBulkCopy,同时防止重复行,没有主键?或者有任何建议以不同的方式做到这一点?
鉴于您使用的是SQL 2008,您可以轻松解决问题,而无需更改应用程序(如果有的话).
第一种可能的解决方案是创建第二个表,就像第一个表一样,但是使用了一个代理身份密钥和一个使用ignore_dup_key选项添加的唯一性约束,这将完成消除重复项的所有重要工作.
以下是您可以在SSMS中运行以查看正在发生的情况的示例:
if object_id( 'tempdb..#test1' ) is not null drop table #test1;
if object_id( 'tempdb..#test2' ) is not null drop table #test2;
go
-- example heap table with duplicate record
create table #test1
(
col1 int
,col2 varchar(50)
,col3 char(3)
);
insert #test1( col1, col2, col3 )
values
( 250, 'Joe''s IT Consulting and Bait Shop', null )
,( 120, 'Mary''s Dry Cleaning and Taxidermy', 'ACK' )
,( 250, 'Joe''s IT Consulting and Bait Shop', null ) -- dup record
,( 666, 'The Honest Politician', 'LIE' )
,( 100, 'My Invisible Friend', 'WHO' )
;
go
-- secondary table for removing duplicates
create table #test2
(
sk int not null identity primary key
,col1 int
,col2 varchar(50)
,col3 char(3)
-- add a uniqueness constraint to filter dups
,constraint UQ_test2 unique ( col1, col2, col3 ) with ( ignore_dup_key = on )
);
go
-- insert all records from original table
-- this should generate a warning if duplicate records were ignored
insert #test2( col1, col2, col3 )
select col1, col2, col3
from #test1;
go
Run Code Online (Sandbox Code Playgroud)
或者,您也可以在没有第二个表的情况下就地删除重复项,但性能可能太慢,无法满足您的需求.这是该示例的代码,也可以在SSMS中运行:
if object_id( 'tempdb..#test1' ) is not null drop table #test1;
go
-- example heap table with duplicate record
create table #test1
(
col1 int
,col2 varchar(50)
,col3 char(3)
);
insert #test1( col1, col2, col3 )
values
( 250, 'Joe''s IT Consulting and Bait Shop', null )
,( 120, 'Mary''s Dry Cleaning and Taxidermy', 'ACK' )
,( 250, 'Joe''s IT Consulting and Bait Shop', null ) -- dup record
,( 666, 'The Honest Politician', 'LIE' )
,( 100, 'My Invisible Friend', 'WHO' )
;
go
-- add temporary PK and index
alter table #test1 add sk int not null identity constraint PK_test1 primary key clustered;
create index IX_test1 on #test1( col1, col2, col3 );
go
-- note: rebuilding the indexes may or may not provide a performance benefit
alter index PK_test1 on #test1 rebuild;
alter index IX_test1 on #test1 rebuild;
go
-- remove duplicates
with ranks as
(
select
sk
,ordinal = row_number() over
(
-- put all the columns composing uniqueness into the partition
partition by col1, col2, col3
order by sk
)
from #test1
)
delete
from ranks
where ordinal > 1;
go
-- remove added columns
drop index IX_test1 on #test1;
alter table #test1 drop constraint PK_test1;
alter table #test1 drop column sk;
go
Run Code Online (Sandbox Code Playgroud)
为什么不简单地使用而不是主键,创建一个索引并设置
Ignore Duplicate Keys: YES
Run Code Online (Sandbox Code Playgroud)
这将防止任何重复的键引发错误,并且不会创建它(因为它已经存在)。

我使用这种方法每天插入大约 120.000 行并且工作完美。