Wie Paul schreibt:Nein, es ist nicht sicher , für die ich empirische Beweise hinzufügen möchte:Erstellen Sie eine Tabelle Table_1
mit einem Feld ID
und einen Datensatz mit dem Wert 0
. Führen Sie dann den folgenden Code gleichzeitig in zwei Management Studio-Abfragefenstern aus :
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
Dann ausführen
SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1
Auf meinem SQL Server 2008 ist eine ID (662
) wurde zweimal erstellt. Daher ist die auf einzelne Anweisungen angewendete Standardisolationsstufe nicht ausreichend.
BEARBEITEN:Klar, Umbruch des INSERT
mit BEGIN TRANSACTION
und COMMIT
wird es nicht beheben, da die Standardisolationsstufe für Transaktionen immer noch READ COMMITTED
ist , was nicht ausreicht. Beachten Sie, dass Sie die Transaktionsisolationsstufe auf REPEATABLE READ
setzen ist auch nicht ausreichend. Die einzige Möglichkeit, den obigen Code sicher zu machen ist hinzuzufügen
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
oben. Dies führte jedoch bei meinen Tests hin und wieder zu Deadlocks.
BEARBEITEN:Die einzige Lösung, die ich gefunden habe, ist und sicher keine Deadlocks erzeugt (zumindest in meinen Tests), ist es, die Tabelle explizit exklusiv zu sperren (hier reicht die Standard-Transaktionsisolationsstufe aus). Aber Vorsicht; diese Lösung könnte töten Leistung:
...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...