SqlBulkCopy: в чем разница между передачей SqlBulkCopyOptions.UseInternalTransaction и отказом от ее передачи?

Я пытаюсь найти разницу между использованием SqlBulkCopy с опцией копирования SqlBulkCopyOptions.UseInternalTransaction и без нее, но в своем тестовом приложении я не обнаруживаю никакой разницы. Если BatchSize, например, равно 0, и я добавляю 100 записей (в DataTable), где запись номер 50 вызывает ошибку при добавлении ее в таблицу базы данных, я получаю 0 записей в таблице. Например, если для BatchSize установлено значение 10, я получаю 40 записей (4 пакета по 10 записей, пятый пакет включает ошибочную запись и вызывает прерывание массового копирования). Неважно, установлен SqlBulkCopyOptions.UseInternalTransaction или нет, я всегда получаю один и тот же результат. Похоже, что пакеты всегда копируются во внутренней транзакции.

Если вас заинтересовало мое тестовое приложение, вот оно: SqlBulkCopy-Error- and-Transaction-Test.zip

Мои вопросы:

  1. Является ли SqlBulkCopyOptions.UseInternalTransaction устаревшим, потому что SqlBulkCopy всегда использует внутренние транзакции?
  2. Если нет: каково фактическое значение этой опции? В каких случаях это будет иметь значение?

Надеюсь, кто-нибудь прояснит

Редактировать: Судя по ответу и комментариям, я предполагаю, что моя проблема недостаточно ясна. Я знаю документацию. В нем говорится, что «По умолчанию операция массового копирования является отдельной транзакцией». и что каждый пакет использует свою собственную транзакцию при передаче UseInternalTransaction. Но если это означает, что по умолчанию операция массового копирования использует только одну транзакцию для всего массового копирования (а не по одной для каждого пакета), я не получил бы записи в базе данных, если бы я установил BatchSize на определенный размер и пакет, который лежит после первый вызывает ошибку. Если бы использовалась только одна транзакция, все записи, добавленные в журнал транзакций, были бы отброшены. Но я получаю записи партий, которые лежат перед партией, включающей ошибочную запись. В соответствии с этим кажется, что по умолчанию каждый пакет выполняется в своей собственной транзакции. Это означает, что не имеет значения, сдам ли я UseInternalTransaction или нет. Если я на неправильном пути, я был бы очень признателен, если бы кто-нибудь мог уточнить.

Один факт может быть важен: я использую SQL Server 2012. Возможно, SQL Server 2008 ведет себя по-другому. Я проверю это.

Редактировать: Благодаря ответу от usr я думаю, что нашел ответ: я немного отладил и профилировал и обнаружил, что приватное поле _internalTransaction действительно не установлено, если UseInternalTransaction не определен. Затем SqlBulkCopy не использует собственную (внутреннюю) транзакцию. Но профилирование показало, что SqlBulkCopy использует TDS (Tabular Data Stream) для копирования данных (независимо от размера BatchSize). Я не нашел много информации о TDS, особенно для SQL Server, но я предполагаю, что SQL Server выполняет операции массового копирования TDS во внутренней транзакции. Поэтому UseInternalTransaction кажется избыточным для SQL Server, но на всякий случай я бы установил его.


person Jürgen Bayer    schedule 13.04.2012    source источник
comment
msdn.microsoft.com/en-us/ библиотека/tchktcdk%28v=vs.80%29.aspx   -  person Tim Schmelter    schedule 13.04.2012
comment
Спасибо, Тим. Да, в документации указано, что по умолчанию операция массового копирования является собственной транзакцией. Чуть позже в нем говорится, что вы можете явно указать параметр UseInternalTransaction в конструкторе класса SqlBulkCopy, чтобы явно вызвать выполнение операции массового копирования в своей собственной транзакции, в результате чего каждый пакет операции массового копирования будет выполняться в рамках отдельной транзакции. Это смущает. И мое тестовое приложение не показывает ни единой разницы! Смотрите мой комментарий к ответу от Ариона. Только если я передаю собственную транзакцию, я добавляю 0 записей в случае ошибки. Попробуйте мое тестовое приложение...   -  person Jürgen Bayer    schedule 13.04.2012
comment
Тим Шмельтер: Ссылка не имеет значения. Это не проясняет вопрос о том, как записать пакет на сервер без транзакции. Если я понимаю вопрос ОП, речь идет не о том, как сделать всю операцию массового копирования транзакцией. Я подтверждаю вывод Юргена. UseInternalTransaction в этом случае не действует.   -  person JohnC    schedule 08.02.2015
comment
Итак, я только что проверил это с помощью SQL Profiler, и, насколько я вижу, разница между использованием UseInternalTransaction и его неиспользованием заключается в следующем: если вы не укажете это, BulkCopy использует режим автоматической фиксации транзакций по умолчанию (т. е. без BEGIN TRAN). / COMMIT TRAN), если вы используете UseInternalTransactions, то вокруг оператора BulkCopy есть явные инструкции BEGIN TRAN / COMMIT TRAN.   -  person Steve Ford    schedule 17.02.2015
comment
Я должен был добавить, что на самом деле это означает, что они фактически одинаковы.   -  person Steve Ford    schedule 17.02.2015


Ответы (1)


Если вы установите этот параметр, класс SQLBulkCopy добавит

_internalTransaction = _connection.BeginTransaction();

вокруг каждой партии.

Но этот параметр не имеет практического значения для SQL Server, поскольку транзакции по умолчанию все равно выполняются в режиме автоматической фиксации.

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

Следующее будет успешным и откатит все партии

var transaction = sourceConnection.BeginTransaction();             
using (SqlBulkCopy bulkCopy =
    new SqlBulkCopy(sourceConnection, SqlBulkCopyOptions.Default, transaction))

{
    bulkCopy.BatchSize = 50;

    bulkCopy.DestinationTableName = "dbo.foobar";
        bulkCopy.WriteToServer(dt);
}

transaction.Rollback();

Прохождение SqlBulkCopyOptions.UseInternalTransaction завершается с ошибкой

Не следует одновременно указывать SqlBulkCopyOption.UseInternalTransaction и передавать внешнюю транзакцию.

Я задавался вопросом, может ли это иметь значение, если SET IMPLICIT_TRANSACTIONS ON; ранее выполнялся для соединения, чтобы отключить режим автоматической фиксации, но перегрузка конструктора SqlBulkCopy, который принимает объект соединения, возвращает «Неожиданную существующую транзакцию». ошибка в любом случае в обоих случаях - и перегрузка, которая принимает строку подключения, просто создает новое подключение.

person Martin Smith    schedule 05.04.2015