Запрос SQL Server, возвращающий несколько строк - PullRequest
3 голосов
/ 15 февраля 2010

В настоящее время я работаю над пакетом служб SSIS, который извлекает таблицу из одной базы данных в другую. Таблицы в обеих базах данных используют тот же столбец, что и первичный ключ. Мой оператор выбора для извлечения данных является простым оператором выбора. Когда я запустил пакет, я получил сообщение об ошибке, где есть дубликаты значений первичного ключа.

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

Я добавил подсказку NOLOCK в свой оператор select, и он перестал возвращать повторяющиеся строки.

Итак, мой вопрос - почему? Я ожидал бы, что оператор select с подсказкой таблицы NOLOCK будет иметь больше шансов на возврат дублирующихся строк, потому что он не использует блокировку, и что оператор select без подсказки NOLOCK должен использовать блокировку, чтобы убедиться, что он не возвращает дубликат строки.

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

SELECT pe.enc_id,
       pe.enc_nbr,
       pe.billable_ind,
       pe.clinical_ind AS clinical_ind,
       pe.budget_ind,
       pe.print_stmt_ind,
       pe.send_coll_letter_ind,
       pe.outsource_exempt_ind,
       cb.First_name + ' ' + cb.last_name AS CreatedBy,
       pe.create_timestamp AS create_timestamp,
       mb.first_name + ' ' + mb.last_name AS ModifiedBy,
       pe.modify_timestamp AS modify_timestamp
FROM   patient_encounter pe WITH(NOLOCK) 
       LEFT OUTER JOIN user_mstr cb WITH(NOLOCK) ON
           pe.created_by = cb.user_id
       LEFT OUTER JOIN user_mstr mb WITH(NOLOCK) ON
           pe.modified_by = mb.user_id

Ответы [ 2 ]

1 голос
/ 15 февраля 2010

Подсказка NOLOCK вызывает грязные аномалии чтения, и одной из таких аномалий является повторное чтение. Такие чтения происходят часто, если обновление изменяет позицию строки в индексе, отсканированном запросом:

  • скажем, у вас есть 2 строки в таблице, с ключом ID, строки со значениями ключа 1 и 2
  • один запрос (T1) запускает таблицу ОБНОВЛЕНИЯ ключ SET = 3 ключ WHERE = 1;
  • второй запрос (T2) запускает SELECT ... FROM таблицы WITH (NOLOCK);
  • T1 блокирует строку со значением ключа 1
  • T2 игнорирует блокировку T1 и читает строку со значением ключа 1
  • T2 продолжает и читает строку со значением ключа 2
  • T1 обновляет строку, и строка перемещается в индексе в новую позицию для значения ключа 3
  • T2 продолжает сканирование и читает строку со значением ключа 3

Таким образом, SELECT прочитал строку дважды, один раз, когда он имел значение ключа 1, и один раз, когда он имел значение ключа 3. Это просто тривиальный пример того, что может произойти. В действительности более сложные запросы могут запускать сложные планы и использовать другие индексы, все из которых подвержены таким аномалиям.

Короче говоря: намек НОЛОКА - это зло. Если вы хотите избежать конфликтов, используйте изоляцию снимка .

0 голосов
/ 15 февраля 2010

Подсказка WITH NOLOCK просто указывает серверу базы данных игнорировать блокировки и просто выбирать текущие значения из базы данных - следовательно, он просто выбирает все значения текущей строки в тот момент, когда достигает этой строки.

Обратите внимание, что вы НЕ получите обновления в новой таблице обновляемых строк.

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

Блокировка всей таблицы предотвратит изменения / дубликаты, но вы рискуете заблокировать всех вне таблицы, пока вы делаете выбор.

EDIT: FYI АЛЬТЕРНАТИВЫ: используйте READPAST - строки, заблокированные другими процессами, пропускаются и TABLOCK - блокировка на уровне таблицы, которая, конечно, блокирует другие процессы и может быть нежелательна.

ПРИМЕЧАНИЕ. UPDLOCK преобразуется в XLOCK во время записи транзакции.

Для подсказок есть две категории: гранулярность и уровень изоляции. Гранулярность включает в себя PAGLOCK, NOLOCK, ROWLOCK и TABLOCK. Подсказки уровня изоляции включают HOLDLOCK, NOLOCK, READCOMMITTED, REPEATABLEREAD и SERIALIZABLE. Можно использовать максимум по одному из каждой группы.

EDIT2: просто для полноты: READCOMMITTED - чтение только данных из транзакций, которые были зафиксированы. Это поведение по умолчанию.

РЕДАКТИРОВАТЬ3: Дополнительная информация: NOLOCK будет читать строки, но вы рискуете прочитать «грязные» данные, которые изменятся или будут иметь данные, которых не будет, если произойдет ROLLBACK с транзакцией, которая может повлиять на точность выбранного набора.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...