Nolockが「更新または削除ステートメントのターゲットテーブルに適用される句で」無視されるのはなぜですか?
-
29-09-2019 - |
質問
私はBOLフレーズに混乱しています:
「挿入、更新、または削除操作によって変更されたテーブルには、readuncommittedおよびnolockを指定することはできません。SQLサーバークエリオプティマイザーは、更新または削除ステートメントのターゲットテーブルに適用される句に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;
また、エラーや警告なしに実行されます。
同等ですか?
テーブルは同じですが、そこから論理的にはソーステーブルであり、ターゲットテーブルではなく、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
なぜノロックは別のテーブルで無視されるべきですか?
または、それが間違っている場合は質問です
更新の書き方1)および2)物理テーブルは同じですが、論理的にはソース(選択)テーブルとターゲットであるため、「更新または削除ステートメントのターゲットテーブルに適用される句のnolockヒント」 (更新)テーブルは異なるものです。
(nolock)が無視されていることを示す更新ステートメントを書く方法は?
なぜそれはまったく無視されるべきですか?それは無視されていますか?
または、それが間違った質問である場合、
なぜ構文は無視されることが保証されているヒントを許可するのですか?
繰り返しになりますが、ドキュメントで書かれたような声明を書くことは不可能です(または)、または「無視する」という感覚を理解していません(それを無視する感覚は何ですか? ...
update2:
回答は、NolockがBOL Docs [1]によって主張されている更新声明の条項で無視されていないことを示しています。
まあ、この質問の本質:
更新声明の条項からノロックを無視することを私に(コンテキスト)を教えてください。
[ 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
構文も同様です - 外部クエリに1つのILがある場合、またはトランザクション内に1つのクエリがありますが、内部クエリには別のILを使用したい場合、これは実行できます(内側クエリで異なるモジュレーターを使用)
- したがって、IL3で実行するトランザクションを作成し、
SELECT
その中で、IL0またはIL1で実行されます
別々に:
- ロックが自動であるため、自分がしている、またはやりたいと思っていることに関係なく、
ISOLATION LEVEL 3
は 必要 にとってUPDATES
とDELETES
, 、そこにREAD UNCOMMITTED
とNOLOCK
適用しないでください、そして使用できません、あなたがそれらを使用した場合、サーバーはそれらを無視します
1つのセッション(SSMSのWindows)で2つの同一のテーブルテストとTestDup [1]を作成して埋めた
--2)
begin tran
Select Txt from TestDUP with(TABLOCKX)
WHERE ID=1
--rollback
たとえば、同じテーブルの別のセッション(SSMSウィンドウ)から選択するブロック(SSMSウィンドウ)
--3.1)
select * from TestDUP
だがしかし
--3.2)
select * from TestDUP WITH(NOLOCK)
3.1)はブロックされているが、3.2)はそうではないことに注意してください。
ただし、testdupからSelectを使用して別のテーブルテストを更新する
--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が無視されていないので、それはドキュメントと矛盾していますよね?
[ 1 ]
同一に満たされた2つのテーブルテストと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;