パウロが書いているように:いいえ、それは安全ではありません 、経験的証拠を追加したい:テーブルを作成する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...