Ich hatte immer etwas Ähnliches verwendet, um dies zu erreichen:
INSERT INTO TheTable
SELECT
@primaryKey,
@value1,
@value2
WHERE
NOT EXISTS
(SELECT
NULL
FROM
TheTable
WHERE
PrimaryKey = @primaryKey)
... aber einmal unter Last ist eine Primärschlüsselverletzung aufgetreten. Dies ist die einzige Anweisung, die überhaupt in diese Tabelle eingefügt wird. Bedeutet dies also, dass die obige Aussage nicht atomar ist?
Das Problem ist, dass es fast unmöglich ist, dies nach Belieben wiederherzustellen.
Vielleicht könnte ich es wie folgt ändern:
INSERT INTO TheTable
WITH
(HOLDLOCK,
UPDLOCK,
ROWLOCK)
SELECT
@primaryKey,
@value1,
@value2
WHERE
NOT EXISTS
(SELECT
NULL
FROM
TheTable
WITH
(HOLDLOCK,
UPDLOCK,
ROWLOCK)
WHERE
PrimaryKey = @primaryKey)
Obwohl ich vielleicht die falschen Schlösser benutze oder zu viele Schlösser oder so.
Ich habe andere Fragen auf stackoverflow.com gesehen, bei denen die Antworten ein "IF (SELECT COUNT (*) ... INSERT" usw.) vorschlagen, aber ich war immer unter der (möglicherweise falschen) Annahme, dass eine einzelne SQL-Anweisung atomar sein würde.
Hat jemand irgendwelche Ideen?
WHEN MATCHED
Klausel zu verwenden?MERGE
Aussage ansehen . Funktioniert es in diesem Fall besser?Antworten:
Was ist mit dem "JFDI" -Muster?
BEGIN TRY INSERT etc END TRY BEGIN CATCH IF ERROR_NUMBER() <> 2627 RAISERROR etc END CATCH
Im Ernst, dies ist am schnellsten und gleichzeitig ohne Sperren, insbesondere bei hohen Lautstärken. Was ist, wenn das UPDLOCK eskaliert und die gesamte Tabelle gesperrt ist?
Lesen Sie Lektion 4 :
quelle
INSERT
(ohne dieEXISTS
) besser funktioniert (dh versuchen Sie, egal was einzufügen, und ignorieren Sie einfach den Fehler 2627).Ich habe HOLDLOCK hinzugefügt, das ursprünglich nicht vorhanden war. Bitte ignorieren Sie die Version ohne diesen Hinweis.
Für mich sollte dies ausreichen:
INSERT INTO TheTable SELECT @primaryKey, @value1, @value2 WHERE NOT EXISTS (SELECT 0 FROM TheTable WITH (UPDLOCK, HOLDLOCK) WHERE PrimaryKey = @primaryKey)
Wenn Sie eine Zeile tatsächlich aktualisieren möchten, falls vorhanden, und einfügen, wenn dies nicht der Fall ist, ist diese Frage möglicherweise hilfreich.
quelle
select 'Done.' where exists(select 0 from foo_testing with(updlock) where id = 4);
, sofernid=4
nicht vorhanden , nicht miteinander in Konflikt stehen, was bedeutet, dass meine ursprüngliche Antwort tatsächlich falsch war. Die Lösung besteht darin, denHOLDLOCK
Hinweis hinzuzufügen . Siehe die bearbeitete Antwort. Danke, dass du mich nervt :)Sie könnten MERGE verwenden:
MERGE INTO Target USING (VALUES (@primaryKey, @value1, @value2)) Source (key, value1, value2) ON Target.key = Source.key WHEN MATCHED THEN UPDATE SET value1 = Source.value1, value2 = Source.value2 WHEN NOT MATCHED BY TARGET THEN INSERT (Name, ReasonType) VALUES (@primaryKey, @value1, @value2)
quelle
Ich weiß nicht, ob dies der "offizielle" Weg ist, aber Sie könnten es versuchen
INSERT
und zurückgreifen,UPDATE
wenn es fehlschlägt.quelle
Erstens ein großes Dankeschön an unseren Mann @gbn für seine Beiträge zur Community. Ich kann gar nicht erklären, wie oft ich seinem Rat folge.
Wie auch immer, genug Fanboying.
Um seine Antwort etwas zu ergänzen, "verbessern" Sie sie vielleicht. Für diejenigen, die sich wie ich unwohl fühlen, was in dem
<> 2627
Szenario zu tun ist (und nein, ein LeerzeichenCATCH
ist keine Option). Ich habe dieses kleine Nugget von Technet gefunden .BEGIN TRY INSERT etc END TRY BEGIN CATCH IF ERROR_NUMBER() <> 2627 BEGIN DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; SELECT @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(); RAISERROR ( @ErrorMessage, @ErrorSeverity, @ErrorState ); END END CATCH
quelle
Ich habe in der Vergangenheit eine ähnliche Operation mit einer anderen Methode durchgeführt. Zuerst deklariere ich eine Variable, die den Primärschlüssel enthält. Dann fülle ich diese Variable mit der Ausgabe einer select-Anweisung, die nach einem Datensatz mit diesen Werten sucht. Dann mache ich und IF-Anweisung. Wenn der Primärschlüssel null ist, fügen Sie ihn ein, andernfalls geben Sie einen Fehlercode zurück.
DECLARE @existing varchar(10) SET @existing = (SELECT primaryKey FROM TABLE WHERE param1field = @param1 AND param2field = @param2) IF @existing is not null BEGIN INSERT INTO Table(param1Field, param2Field) VALUES(param1, param2) END ELSE Return 0 END
quelle