为什么“在应用于 UPDATE 或 DELETE 语句的目标表的 FROM 子句中”忽略 NOLOCK?

发布于 2024-10-05 12:50:05 字数 1569 浏览 6 评论 0原文

我对 BOL 短语感到困惑:

“不能为通过插入、更新或删除操作修改的表指定 READUNCOMMITTED 和 NOLOCK。SQL Server 查询优化器会忽略适用于 UPDATE 或 DELETE 语句的目标表的 FROM 子句中的 READUNCOMMITTED 和 NOLOCK 提示” [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?
或者,如果错了,请提问
如何编写具有“FROM 子句中适用于 UPDATE 或 DELETE 语句的目标表的 NOLOCK 提示”的 UPDATE,因为即使在 1) 和 2) 中,物理表也是相同的,但逻辑上是源(在 SELECT 中)表和目标(在更新中)表是不同的。

如何编写一条 UPDATE 语句来证明WITH(NOLOCK) 被忽略?
为什么要完全忽略它呢?被忽略了吗?
或者,如果这是一个错误的问题,那么
为什么语法允许保证被忽略的提示?

再说一次,要么不可能(或者是?)写出文档中写的这样的语句,要么我不理解“忽略”的含义(忽略它是什么意思?或者根本拥有它?) ...

更新2:
答案表明,在 UPDATE 语句的 FROM 子句中,NOLOCK 并未(更新)被忽略,这是 BOL 文档 [1] 所断言的。
好吧,这个问题的本质是:
你能给我任何例子(上下文),其中忽略 UPDATE 语句的 FROM 子句中的 NOLOCK 是有意义的吗?

[ 1 ]
表提示 (Transact-SQL)
SQL Server 2008 R2
http://msdn.microsoft.com/en-us/library/ms187373.aspx

I am confused by the BOL phrase:

"READUNCOMMITTED and NOLOCK cannot be specified for tables modified by insert, update, or delete operations. The SQL Server query optimizer ignores the READUNCOMMITTED and NOLOCK hints in the FROM clause that apply to the target table of an UPDATE or DELETE statement" [ 1 ]

For example, if I write

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

it is run without errors (or warnings) and is probably equivalent to

--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;

which is also run without errors or warnings.
Is it equivalent?

The table is the same but in FROM it is logically the source table not the target table
I could have re-written 1) with a different source table as another (physical) table:

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

GO;

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

Why should NOLOCK be ignored on another table?
Or, if it is wrong, question then
How to write UPDATE having "NOLOCK hints in the FROM clause that apply to the target table of an UPDATE or DELETE statement" because even in 1) and 2) the physical table is the same but logically the source (in SELECT) table and target (in UPDATE) table are different ones.

How to write an UPDATE statement demonstrating that WITH(NOLOCK) is ignored?
Why should it be ignored at all? Is it ignored?
Or, if it is a wrong question, then
Why does syntax permit the hint which is guaranteed to be ignored?

Once again, either it is impossible (or is it?) to write such a statement as written in documentation or I do not understand the sense of "ignores" (What is the sense to ignore it? or to have it at all?)...

UPDATE2:
The answers show that NOLOCK is NOT (updated) ignored in the FROM clause of UPDATE statement what is asserted by BOL docs [ 1 ].
Well, the essence of this question:
Can you give me any example (context) where ignoring of NOLOCK in FROM clause of UPDATE statement would have made sense?

[ 1 ]
Table Hints (Transact-SQL)
SQL Server 2008 R2
http://msdn.microsoft.com/en-us/library/ms187373.aspx

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

烦人精 2024-10-12 12:50:05

无需猜测。

Sybase 和 MS SQL 服务器使用内部自动 2PL 资源锁定,但完全符合 ISO/IEC/ANSI SQL 标准。当您尝试理解所有可能的组合时,语法会变得很愚蠢,因为某些子句并不与每个命令相关。

该手册试图表达的内容(但不是用简单的英语表达)是:

  • 对于您正在执行的任何外部操作或事务中的单个查询,您可以SET ISOLATION LEVEL
  • 使用 UNCOMMITTED、NOLOCK、HOLDLOCK 语法指定,以及
  • 在外部查询中有一个 IL 或事务中的单个查询,但希望对内部查询使用不同的 IL 时,可以是完成(在内部查询上使用不同的调制器),
  • 这样您就可以在 IL3 上执行一个事务,并在其中有一个 SELECT

分别在 IL0 或 IL1 上执行:

  • 无论您认为自己在做什么,或者因为锁定是自动的,并且 UPDATESDELETES 需要隔离级别 3,其中READ UNCOMMITTEDNOLOCK 不适用,并且不能使用,如果您使用了它们,服务器将忽略它们

No guessing required.

Sybase and MS SQL server use an internal, automatic 2PL resource locking, but with full compliance with the ISO/IEC/ANSI SQL Standard. The syntax gets silly when you try to understand all possible combinations, because some clauses are not relevant for every command.

What the manual is trying to say, but does not say in simple English, is:

  • for whatever outer operation, or a single query within a transaction, you are performing, you can SET ISOLATION LEVEL
  • that can be specified using UNCOMMITTED, NOLOCK, HOLDLOCKsyntax as well
  • where you have one IL in the outer query, or a single query within a transaction, but want to use a different IL for the inner query, that can be done (use different modulators on the inner query)
  • so you could have a transaction executing at IL3, and have one SELECT within it executing at IL0 or IL1

Separately:

  • regardless of what you think you are doing, or want to do, since the locking is automatic, and ISOLATION LEVEL 3is required for UPDATES and DELETES, wherein READ UNCOMMITTED and NOLOCK do not apply, and cannot be used, if you have used them the server will ignore them
最偏执的依靠 2024-10-12 12:50:05

UPDATE 或 DELETE 语句的 FROM 子句在您的任何示例中都不明显。子查询中有 from 子句,但它们不是同一回事。

这是 UPDATE 的 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 子句对,并且可以查询的其余部分保持不变。


根据 vgv8 的评论/“答案”进行更新:

您的示例更新仍然没有查看 UPDATE 语句的 FROM 子句

即使在另一个连接上打开 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

The FROM clause of an UPDATE or DELETE statement isn't evident in any of your examples. You have from clauses in subqueries, but those aren't the same thing.

Here's a FROM clause for an UPDATE:

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

And, as the documentation calls out, the WITH (NOLOCK) will be ignored in this case. As to why this is allowed if it's going to be ignored, one guess would be that such a hint would be valid in the SELECT version of the "same" query, and people do frequently write SELECTs (to ensure they're targetting the correct rows/columns), and then replace the SELECT clause with a UPDATE/SET pair of clauses, and can leave the rest of the query unaltered.


Updated based on comment/"answer" from vgv8:

Your example update still isn't looking at the FROM clause of the UPDATE statement

The following works fine, even with the TABLOCKX() open on the other connection:

UPDATE T  SET Txt= td.Txt
FROM TEST t inner join TESTDUP td  WITH (NOLOCK) on t.ID = td.ID
where t.ID = 1
二货你真萌 2024-10-12 12:50:05

创建并填充 2 个相同的表 Test 和 TestDUP [1] 后,在一个会话(SSMS 窗口)中,我执行

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

阻止来自同一表上的另一个会话(SSMS 窗口)的 SELECT,例如:

 --3.1)
select * from TestDUP

但不注意

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

3.1)被阻止但 3.2) 则不然。

但是,使用 TestDUP 中的 SELECT 更新另一个表 TEST

--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;

会被阻止,因为 UPDATE 语句的 FROM 子句中忽略另一个源表上的WITH(NOLOCK)。

更新:

--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  

所以,它现在是有道理的,但它与文档相矛盾,因为 UPDATE 语句的 FROM 子句中的 NOLOCK 不会被忽略,不是吗?

[ 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;

Having created and filled 2 identical tables Test and TestDUP [ 1 ], in one session (windows of SSMS) I execute

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

which blocks SELECT from another session (SSMS window) on the same table, for example:

 --3.1)
select * from TestDUP

but not

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

Note that 3.1) is blocked but 3.2) is not.

Though, updating on another table TEST using 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;

is blocked because WITH(NOLOCK), on another source table, is ignored in FROM clause of UPDATE statement.

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  

So, it now makes sense but it contradicts to documentation since NOLOCK in FROM clause of UPDATE statement is NOT ignored, does not it?

[ 1 ]
Create 2 identically filled tables Test and 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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文