为什么“在适用于更新或删除语句的目标表的“从子句中”中忽略了Nolock?
-
29-09-2019 - |
题
我对bol短语感到困惑:
“无法为通过插入,更新或删除操作修改的表指定READUNCOMMINT和NOLOCK。SQL SERVER查询优化器忽略了适用于更新或删除语句的目标表中的“从子句”中的ReadUncommitting和Nolock提示” [1] [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;
也没有错误或警告。
它等效吗?
该表是相同的,但从逻辑上讲,源表不是我可以重写的目标表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提示”,因为即使在1)和2)物理表是相同的,但从逻辑上讲是源(在选择)表和目标。 (在更新中)表是不同的。
如何编写一个更新语句,证明(nolock)被忽略了?
为什么要忽略它?它被忽略了吗?
或者,如果这是一个错误的问题,那就
为什么语法允许保证被忽略的提示?
再说一次,要么写下文档中写的陈述是不可能的(或者是吗?),要么我不理解“忽略”的感觉(忽略它的感觉是什么? ...
Update2:
答案表明,在Update语句的“来自BOL DOCS [1]所主张的内容的内容中,Nolock并未忽略(更新)。
好吧,这个问题的本质:
您能给我任何示例(上下文),在更新语句的条款中忽略了nolock是有道理的吗?
[ 1 ]
表提示(Transact-SQL)
SQL Server 2008 R2
http://msdn.microsoft.com/en-us/library/ms187373.aspx
解决方案
在您的任何示例中,更新或删除语句的从条款均不明显。您从子征中的子句中有,但这不是同一回事。
这是一个从条款中进行更新:
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
条款与 UPDATE
/SET
一对条款,可以使其余的查询不变。
根据评论/VGV8的“答案”更新:
您的示例更新仍然不在查看 更新语句的子句
以下工作正常,即使在另一个连接上打开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
其他提示
无需猜测。
Sybase和MS SQL Server使用内部的自动2PL资源锁定,但完全符合ISO/IEC/ANSI SQL标准。当您尝试理解所有可能的组合时,语法会变得愚蠢,因为某些子句与每个命令无关。
手册试图说的话,但并不是用简单的英语说的是:
- 对于任何外部操作或交易中的单个查询,您都可以执行
SET ISOLATION LEVEL
- 可以使用
UNCOMMITTED, NOLOCK, HOLDLOCK
语法也是如此 - 如果您在外部查询中有一个IL,或者在事务中有一个查询,但想在内部查询中使用其他IL,可以完成(在内部查询上使用不同的调节器)
- 因此,您可以在IL3处执行一项交易,并有一个
SELECT
内部执行IL0或IL1
分别地:
- 无论您认为您在做什么或想做什么,因为锁定是自动的,并且
ISOLATION LEVEL 3
是 必需的 为了UPDATES
和DELETES
, ,其中READ UNCOMMITTED
和NOLOCK
请勿申请,也不能使用,如果您使用了它们,服务器将忽略它们
创建并填写了2个相同的表测试和测试DUP [1],在一个会话(SSM的窗口)中,我执行
--2)
begin tran
Select Txt from TestDUP with(TABLOCKX)
WHERE ID=1
--rollback
哪个封锁同一表上的另一个会话(SSMS窗口)的选择,例如:
--3.1)
select * from TestDUP
但不是
--3.2)
select * from TestDUP WITH(NOLOCK)
请注意,3.1)被阻止,但3.2)却没有。
但是,使用Select从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;
被阻止了,因为在另一个源表上使用(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
因此,现在这是有道理的,但是它与文档相矛盾,因为NOLOCK不忽略更新语句条款的Nolock,不是吗?
[ 1 ]
创建2个相同填充的表测试和测试DUP:
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;