Как предотвратить дублирующиеся записи, вставляемые с SqlBulkCopy, когда нет никакого первичного ключа

Я получаю ежедневный XML-файл, который содержит тысячи записей, при этом каждый - деловая сделка, которую я должен сохранить во внутренней базе данных для использования в создании отчетов и тарификации. У меня создалось впечатление, что файл каждого дня содержал только уникальные записи, но обнаружил, что моим определением уникальных не является точно то же как поставщик.

Текущее приложение, которое импортирует эти данные, является консольным приложением C#.Net 3.5, это делает настолько использующий SqlBulkCopy в таблицу базы данных SQL Server MS 2008 года, где столбцы точно соответствуют структуре записей XML. Каждая запись имеет чуть более чем 100 полей, и нет никакого естественного ключа в данных или скорее полях, я могу придумать то, чтобы иметь смысл, поскольку составной ключ заканчивают тем также, что имели необходимость позволить, аннулирует. В настоящее время таблица имеет несколько индексов, но никакой первичный ключ.

В основном вся строка должна быть уникальной. Если одно поле отличается, это достаточно допустимо, чтобы быть вставленным. Я посмотрел на создание хеша MD5 всей строки, вставка, что в базу данных и использование ограничения, чтобы препятствовать тому, чтобы SqlBulkCopy вставил строку, но я не вижу, как получить Хеш MD5 в деятельность BulkCopy, и я не уверен, привела ли целая операция к сбою и откатывала бы, если любая запись перестала работать, или если это продолжится.

Файл содержит очень большое количество записей, идущей строки строкой в XML, запрашивая базу данных для записи, которая соответствует всем полям, и затем решающий вставить действительно единственный путь I, видят способность сделать это. Я просто надеялся не должным быть переписать приложение полностью, и операция массового копирования настолько быстрее.

Кто-либо знает о способе использовать SqlBulkCopy при предотвращении дублирующихся строк без первичного ключа? Или какое-либо предложение для другого способа сделать это?

17
задан gbn 7 April 2010 в 15:58
поделиться

5 ответов

Я загружал данные в промежуточную таблицу, а затем обрабатывал дубликаты при копировании в итоговую таблицу.

Например, вы можете создать (неуникальный) индекс в промежуточной таблице для работы с «ключом»

17
ответ дан 30 November 2019 в 12:13
поделиться

Я бы скопировал массово во временную таблицу, а затем отправил данные из нее в фактическую целевую таблицу. Таким образом, вы можете использовать SQL для проверки и обработки дубликатов.

4
ответ дан 30 November 2019 в 12:13
поделиться

Каков объем данных? У вас есть 2 варианта, которые я вижу:

1: отфильтровать его в источнике, реализовав свой собственный IDataReader и используя некоторый хеш для данных, и просто пропуская любые дубликаты, чтобы они никогда не попадали в TDS.

2: отфильтровать в БД; на самом простом уровне, я полагаю, вы могли бы иметь несколько этапов импорта - сырые, не подвергнутые анализу данные - а затем скопировать данные DISTINCT в свои фактические таблицы, возможно, используя промежуточную таблицу, если вы хотите. Вы можете использовать КОНТРОЛЬНУЮ СУММ для некоторых из них, но это зависит от обстоятельств.

1
ответ дан 30 November 2019 в 12:13
поделиться

И исправить эту таблицу. Ни одна таблица никогда не должна быть без уникального индекса, желательно в виде ПК. Даже если вы добавляете суррогатный ключ из-за отсутствия естественного ключа, вам необходимо иметь возможность конкретно идентифицировать конкретную запись. В противном случае, как вы избавитесь от дубликатов, которые у вас уже есть?

1
ответ дан 30 November 2019 в 12:13
поделиться

Учитывая, что вы используете 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

В качестве альтернативы вы также можете удалить дубликаты на месте без второй таблицы, но производительность может быть слишком низкой для ваших нужд. Вот код этого примера, который также можно запустить в 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
7
ответ дан 30 November 2019 в 12:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: