Ответ 1
Я бы загрузил данные в промежуточную таблицу, а затем обработал дубликаты после копирования в финальную таблицу.
Например, вы можете создать (не уникальный) индекс в промежуточной таблице для обработки "ключа"
Я получаю ежедневный XML файл, содержащий тысячи записей, каждый из которых является бизнес-транзакцией, которую я должен хранить во внутренней базе данных для использования в отчетности и биллинге. У меня создалось впечатление, что каждый день файл содержит только уникальные записи, но обнаружил, что мое определение уникальности не совсем то же самое, что и у провайдера.
Текущее приложение, которое импортирует эти данные, является консольным приложением С#.Net 3.5, оно делает это с использованием SqlBulkCopy в таблице базы данных MS SQL Server 2008, где столбцы точно соответствуют структуре записей XML. Каждая запись имеет только более 100 полей, и в данных нет естественного ключа, или, скорее, поля, которые я могу придумать, как составной ключ, также должны иметь нулевые значения. В настоящее время таблица имеет несколько индексов, но не первичный ключ.
В принципе, вся строка должна быть уникальной. Если одно поле отличается, оно достаточно допустимо для вставки. Я посмотрел на создание хэша MD5 всей строки, вставив его в базу данных и используя ограничение, чтобы предотвратить включение SqlBulkCopy строки, но я не вижу, как получить MD5 Hash в операции 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' IT Consulting and Bait Shop', null )
,( 120, 'Mary' Dry Cleaning and Taxidermy', 'ACK' )
,( 250, 'Joe' 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' IT Consulting and Bait Shop', null )
,( 120, 'Mary' Dry Cleaning and Taxidermy', 'ACK' )
,( 250, 'Joe' 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
Я бы навалом скопировал во временную таблицу, а затем вытащил данные из этой таблицы в фактическую таблицу назначения. Таким образом, вы можете использовать SQL для проверки и обработки дубликатов.
Что такое объем данных? У меня есть два варианта:
1: отфильтруйте его в исходном коде, выполнив свой собственный IDataReader
и используя некоторый хеш по данным, и просто пропустите любые дубликаты, чтобы они никогда не попадали в TDS.
2: отфильтровать его в БД; на простейшем уровне, я думаю, вы могли бы иметь несколько этапов импорта - необработанные, unsanitised данные - и затем скопировать данные DISTINCT
в ваши фактические таблицы, возможно, используя промежуточную таблицу, если хотите. Вы можете использовать CHECKSUM
для некоторых из них, но это зависит.
Я думаю, что это намного чище.
var dtcolumns = new string[] { "Col1", "Col2", "Col3"};
var dtDistinct = dt.DefaultView.ToTable(true, dtcolumns);
using (SqlConnection cn = new SqlConnection(cn)
{
copy.ColumnMappings.Add(0, 0);
copy.ColumnMappings.Add(1, 1);
copy.ColumnMappings.Add(2, 2);
copy.DestinationTableName = "TableNameToMapTo";
copy.WriteToServer(dtDistinct );
}
Таким образом, нужна только одна таблица базы данных и может содержать Bussiness Logic в коде.
И исправьте эту таблицу. Стол никогда не должен быть без уникального индекса, предпочтительно как ПК. Даже если вы добавляете суррогатный ключ, потому что нет естественного ключа, вы должны иметь возможность конкретно идентифицировать конкретную запись. В противном случае, как вы избавитесь от дубликатов, которые у вас уже есть?
Почему бы просто не использовать вместо Первичный ключ создать Индекс и установить
Ignore Duplicate Keys: YES
Это приведет к предотвратить дублирование ключа для устранения ошибки, и он не будет создан (как он уже существует).
Я использую этот метод для вставки около 120 000 строк в день и работает безупречно.