10

Вопрос по c#, ado.net – SqlBulkCopy Обработка ошибок / продолжить при ошибке

Я пытаюсь вставить огромное количество данных в сервер SQL. Моя таблица назначения имеет уникальный индекс, называемый «Hash».

Я хотел бы заменить мою реализацию SqlDataAdapter на SqlBulkCopy. В SqlDataAapter есть свойство & quot; ContinueUpdateOnError & quot ;, при значении true adapter.Update (таблица) вставит все возможные строки и отметит строки ошибок свойством RowError.

Вопрос в том, как я могу использовать SqlBulkCopy для максимально быстрой вставки данных, отслеживая, какие строки были вставлены, а какие - нет (из-за уникального индекса)?

Вот дополнительная информация:

  1. The process is iterative, often set on a schedule to repeat.

  2. The source and destination tables can be huge, sometimes millions of rows.

  3. Even though it is possible to check for the hash values first, it requires two transactions per row (first for selecting the hash from destination table, then perform the insertion). I think in the adapter.update(table)'s case, it is faster to check for the RowError than checking for hash hits per row.

ChrisW, ценю ваше предложение. Если вы предлагаете мне выполнить этот запрос & quot; удалите из таблицы, в которой хэш (значение1, значение2, ..). Я не могу использовать этот подход, потому что он будет обрабатывать каждую строку, как если бы она не существовала в таблице назначения, однако другие части моей системы любят обрабатывать только новые записи (те, которые не содержат коллизии хэшей).

Jun 17, 2009, 4:01 AMот

@Paladin, см расширенный ответ

Jun 17, 2009, 4:52 AMот

Почему бы не удалить строки, которые уже существуют, из набора данных для вставки, прежде чем пытаться вставить?

Jun 17, 2009, 12:24 AMот

3ответа

4

Немного другой подход, чем уже предложено; ВыполнитеSqlBulkCopy и пойматьSqlException брошено:

    Violation of PRIMARY KEY constraint 'PK_MyPK'. Cannot insert duplicate 
key in object 'dbo.MyTable'. **The duplicate key value is (17)**.

Затем вы можете удалить все элементы из вашего источника из идентификатора 17, первой записи, которая была дублирована. Я делаю здесь предположения, которые относятся к моим обстоятельствам и, возможно, не к вашим; то есть, что дублирование вызваноexact те же данные из ранее не удалосьSqlBulkCopy из-за ошибок SQL / Network во время загрузки.

7

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

Тем не менее, это быстро, действительно очень быстро.

Если вы хотите обойти проблему дублирования ключа и определить, какие строки являются дубликатами в пакете. Один из вариантов:

  • start tran
  • Grab a tablockx on the table select all current "Hash" values and chuck them in a HashSet.
  • Filter out the duplicates and report.
  • Insert the data
  • commit tran

Этот процесс будет работать эффективно, если вы вставляете огромные наборы, а размер исходных данных в таблице не слишком велик.

Можете ли вы расширить свой вопрос, чтобы включить остальную часть контекста проблемы.

EDIT

Теперь, когда у меня есть немного контекста, вот еще один способ, которым вы можете сделать это:

  • Do the bulk insert into a temp table.
  • start serializable tran
  • Select all temp rows that are already in the destination table ... report on them
  • Insert the data in the temp table into the real table, performing a left join on hash and including all the new rows.
  • commit the tran

Этот процесс очень легок в поездках туда и обратно, и, учитывая ваши спецификации, он должен быть очень быстрым;

1

Note: This is a recap of Sam's answer with slightly more details

Спасибо Сэму за ответ. Я поместил его в ответ из-за нехватки места для комментариев.

Исходя из вашего ответа я вижу два возможных подхода:

Решение 1:

  • start tran
  • grab all possible hit "hash" values by doing "select hash in destinationtable where hash in (val1, val2, ...)
  • filter out duplicates and report
  • insert data
  • commit tran

Решение 2:

  • Create temp table to mirror the schema of destination table
  • bulk insert into the temp table
  • start serializable transaction
  • Get duplicate rows: "select hash from tempTable where tempTable.hash=destinationTable.hash"
  • report on duplicate rows
  • Insert the data in the temp table into the destination table: "select * into destinationTable from temptable left join temptable.hash=destinationTable.hash where destinationTable.hash is null"
  • commit the tran

Поскольку у нас есть два подхода, все сводится к тому, какой подход наиболее оптимизирован? Оба подхода должны извлекать дубликаты строк и сообщать, в то время как второй подход требует дополнительного:

  • temp table creation and delete
  • one more sql command to move data from temp to destination table
  • depends on the percentage of hash collision, it also transfers a lot of unnecessary data across the wire

Если это единственные решения, мне кажется, что первый подход выигрывает. Что, вы парни, думаете? Спасибо!

RelatedQuestions