Почему NOLOCK игнорируется «в предложении FROM, которое применяется к целевой таблице инструкции UPDATE или DELETE»? - PullRequest
0 голосов

Меня смущает фраза BOL:

"READUNCOMMITTED и NOLOCK нельзя указывать для таблиц, изменяемых операциями вставки, обновления или удаления. Оптимизатор запросов SQL Server игнорирует подсказки READUNCOMMITTED и NOLOCKв предложении FROM, которое применяется к целевой таблице оператора UPDATE или DELETE "[1]

Например, если я пишу

--script 1) 
UPDATE Test SET Txt=(Select Txt from TEST WITH(NOLOCK) where ID=1) 
WHERE ID=1

, он запускается без ошибок (или предупреждения) и, вероятно, эквивалентно

--script 2)
set transaction isolation level SERIALIZABLE;
begin tran
Declare @nvarm nvarchar(max);

Select @nvarm=Txt from Test where ID=1;
--Select @nvarm;
UPDATE Test  SET Txt=@nvarm  WHERE ID=1;
commit;

, который также запускается без ошибок или предупреждений.
Это эквивалентно?

Таблица та же самая, но в FROM она логически является исходной таблицей, а не целевой таблицей, которую я мог бы переписать 1) с другой исходной таблицей в качестве другой (физической) таблицы:

--script 3)
select *
into testDup
from TEST;

GO;

UPDATE Test SET Txt=(SELECT Txt FROM TestDUP WITH(NOLOCK) where ID=1) 
    WHERE ID=1

Почему NOLOCK следует игнорировать в другой таблице?
Или, если это не так, задать вопрос
Как записать UPDATE с подсказками "NOLOCK" в предложении FROM, которые применяются к целевой таблице UPDATE или DELETEутверждение "потому что даже в 1) и 2) физическая таблица одна и та же, но логически исходная (в SELECT) таблица и целевая (в UPDATE) таблица различны.

Как написать инструкцию UPDATE, демонстрирующую, что WITH (NOLOCK) игнорируется?
Почему его вообще следует игнорировать?Это игнорируется?
Или, если это неправильный вопрос, то
Почему синтаксис допускает подсказку, которая гарантированно игнорируется?

Еще раз: либо невозможно (или это так?) Написать такое утверждение, как написано в документации, или я не понимаю смысла "игнорировать" (Какой смысл игнорировать это?у вас это вообще есть?) ...

UPDATE2:
Ответы показывают, что NOLOCK НЕ (обновляется) игнорируется в предложении FROM инструкции UPDATE, что утверждается в документах BOL [1].
Ну, суть этого вопроса:
Можете ли вы привести какой-либо пример (контекст), в котором было бы целесообразно игнорировать NOLOCK в предложении FROM оператора UPDATE?

[1]
Таблица подсказок (Transact-SQL)
SQL Server 2008 R2
http://msdn.microsoft.com/en-us/library/ms187373.aspx

Ответы [ 3 ]

4 голосов
/ 18 января 2011

Не нужно угадывать.

Sybase и сервер MS SQL используют внутреннюю автоматическую блокировку ресурсов 2PL, но с полным соответствием стандарту SQL ISO / IEC / ANSI.Синтаксис становится глупым, когда вы пытаетесь понять все возможные комбинации, потому что некоторые пункты не имеют отношения к каждой команде.

То, что руководство пытается сказать, но не говорит на простом английском, это:

  • для любой внешней операции или отдельного запроса внутри транзакции, который вы выполняете, вы можете SET ISOLATION LEVEL
  • , который также может быть указан с использованием синтаксиса UNCOMMITTED, NOLOCK, HOLDLOCK
  • если у вас есть один IL во внешнем запросе или один запрос внутри транзакции, но вы хотите использовать другой IL для внутреннего запроса, который может быть выполнен (используйте разные модуляторы для внутреннего запроса)
  • так что вы можете иметь транзакцию, выполняющуюся в IL3, и иметь одну SELECT внутри нее, выполняющуюся в IL0 или IL1

Отдельно:

  • независимо от того, что вы думаете о себеделать или хотите делать, поскольку блокировка автоматическая, а ISOLATION LEVEL 3 - это требуется для UPDATES и DELETES, где READ UNCOMMITTED и NOLOCK не применяются и не могут использоватьсяесли у тебя естьsed их сервер игнорирует их
2 голосов
/ 01 декабря 2010

Предложение FROM оператора UPDATE или DELETE не видно ни в одном из ваших примеров.В подзапросах есть предложения from, но это не одно и то же.

Вот предложение FROM для ОБНОВЛЕНИЯ:

UPDATE t
SET Col = u.Val
FROM   /* <-- Start of FROM clause */
   Table t WITH (NOLOCK)
       inner join
   Table2 u
       on
          t.ID = u.ID
/* End of FROM clause */
WHERE
    u.Colx = 19

И, как гласит документация, WITH (NOLOCK) будет игнорироваться в этом случае.Что касается того, почему это разрешено, если оно будет проигнорировано, можно предположить, что такая подсказка будет действительной в SELECT версии «одного и того же» запроса, и люди часто пишут SELECT (чтобы гарантировать, что они нацеленыправильные строки / столбцы), а затем замените предложение SELECT парой предложений UPDATE / SET и оставьте оставшуюся часть запроса без изменений.


Обновлено на основеcomment / "answer" от vgv8:

В вашем примере обновления все еще не просматривается предложение FROM оператора UPDATE

Следующее работает нормально, даже сTABLOCKX () открыть на другом соединении:

UPDATE T  SET Txt= td.Txt
FROM TEST t inner join TESTDUP td  WITH (NOLOCK) on t.ID = td.ID
where t.ID = 1
0 голосов

Создав и заполнив 2 одинаковые таблицы Test и TestDUP [1], в одном сеансе (окна SSMS) я выполняю

--2)
begin tran
Select Txt from TestDUP  with(TABLOCKX) 
WHERE ID=1
--rollback

, который блокирует SELECT из другого сеанса (окна SSMS) в той же таблицеНапример:

 --3.1)
select * from TestDUP

, но не

 --3.2)
select * from TestDUP WITH(NOLOCK)

Обратите внимание, что 3.1) заблокирован, а 3.2) - нет.

Хотя обновление в другой таблице TEST с использованием SELECT from TestDUP

--4)WITH(NOLOCK) is not honored until completing
-- (commit/roollback)-ing transaction 2)
UPDATE Test  SET Txt=
(Select Txt from TESTDUP WITH(NOLOCK)  where ID=1)
  WHERE ID=1;

заблокировано, поскольку WITH (NOLOCK) в другой исходной таблице игнорируется в предложении FROM инструкции UPDATE.

Обновление:

--4.1)WITH(NOLOCK) is honored 
-- in FROM clause of UPDATE statement 
UPDATE Test  SET Txt= td.Txt
FROM TESTDUP td  WITH (NOLOCK)
where test.ID = 1 

--4.2) Note that without NOLOCK this script is blocked
-- until first transaction 2) completes (rollbacks or commits)
UPDATE Test  SET Txt= td.Txt
FROM TESTDUP td  WITH (NOLOCK)
where test.ID = 1  

Итак, теперь это имеет смысл, но это противоречит документации, поскольку NOLOCK в предложении FROM оператора UPDATE НЕ игнорируется,не так ли?

[1]
Создать 2 одинаково заполненные таблицы Test и testDUP:

if object_id('Test') IS not NULL
drop table Test;

CREATE TABLE Test (
  ID int IDENTITY PRIMARY KEY,
  Txt nvarchar(max) NOT NULL
)
GO
-----------
INSERT INTO Test
SELECT REPLICATE(CONVERT(nvarchar(max), 
     CHAR(65+ABS(CHECKSUM(NEWID()))%26)),100000)
GO 10

--COPYING TEST into TESTDUP with creating of the latter
select *
into testDup
from TEST;
...