パウロが書いているように:いいえ、それは安全ではありません 、経験的証拠を追加したい:テーブルを作成するTable_1 1つのフィールドID 値が0の1つのレコード 。次に、2つのManagementStudioクエリウィンドウで同時に次のコードを実行します。 :
declare @counter int
set @counter = 0
while @counter < 1000
begin
set @counter = @counter + 1
INSERT INTO Table_1
SELECT MAX(ID) + 1 FROM Table_1
end
次に実行
SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1
私のSQLServer2008では、1つのID(662 )は2回作成されました。したがって、単一のステートメントに適用されるデフォルトの分離レベルは not 十分です。
編集:明らかに、INSERTをラップします BEGIN TRANSACTIONを使用 およびCOMMIT トランザクションのデフォルトの分離レベルはまだREAD COMMITTEDであるため、修正されません 、これでは不十分です。トランザクション分離レベルをREPEATABLE READに設定することに注意してください も 十分ではありません。上記のコードを安全にする唯一の方法 追加することです
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
頂点で。ただし、これにより、テストでデッドロックが発生することがありました。
編集:私が見つけた唯一の安全な解決策は (少なくとも私のテストでは)デッドロックを生成しないのは、テーブルを明示的に排他的にロックすることです(ここではデフォルトのトランザクション分離レベルで十分です)。ただし、注意してください。このソリューションは殺すかもしれません パフォーマンス:
...loop stuff...
BEGIN TRANSACTION
SELECT * FROM Table_1 WITH (TABLOCKX, HOLDLOCK) WHERE 1=0
INSERT INTO Table_1
SELECT MAX(ID) + 1 FROM Table_1
COMMIT
...loop end...