Ich muss eine gespeicherte T-SQL-Prozedur schreiben, die eine Zeile in einer Tabelle aktualisiert. Wenn die Zeile nicht vorhanden ist, fügen Sie sie ein. All diese Schritte werden von einer Transaktion umschlossen.
Dies ist für ein Buchungssystem, daher muss es atomar und zuverlässig sein . Es muss true zurückgeben, wenn die Transaktion festgeschrieben und der Flug gebucht wurde.
Ich bin neu in T-SQL und nicht sicher, wie ich es verwenden soll @@rowcount
. Das habe ich bis jetzt geschrieben. Bin ich auf dem richtigen Weg? Ich bin sicher, das ist ein leichtes Problem für Sie.
-- BEGIN TRANSACTION (HOW TO DO?)
UPDATE Bookings
SET TicketsBooked = TicketsBooked + @TicketsToBook
WHERE FlightId = @Id AND TicketsMax < (TicketsBooked + @TicketsToBook)
-- Here I need to insert only if the row doesn't exists.
-- If the row exists but the condition TicketsMax is violated, I must not insert
-- the row and return FALSE
IF @@ROWCOUNT = 0
BEGIN
INSERT INTO Bookings ... (omitted)
END
-- END TRANSACTION (HOW TO DO?)
-- Return TRUE (How to do?)
sql
sql-server
sql-server-2008
tsql
Whymarrh
quelle
quelle
Antworten:
Schauen Sie sich den Befehl MERGE an . Sie können tun
UPDATE
,INSERT
undDELETE
in einer Erklärung.Hier ist eine funktionierende Implementierung zur Verwendung
MERGE
- Sie prüft, ob der Flug voll ist, bevor ein Update durchgeführt wird, andernfalls wird eine Einfügung durchgeführt.
Und dann ...
quelle
Ich nehme für jeden Flug eine einzelne Reihe an? Wenn ja:
Ich gehe davon aus, was ich gesagt habe, da Ihre Vorgehensweise einen Flug überbuchen kann, da eine neue Zeile eingefügt wird, wenn maximal 10 Tickets vorhanden sind und Sie 20 buchen.
quelle
BEGIN TRAN ... COMMIT
Isolationsstufe stecken, wird das Problem nicht behoben . Das OP spezifizierte, dass atomare und zuverlässige Anforderungen waren. Ihre Antwort spricht dies in keiner Form an.IF EXISTS (SELECT * FROM Bookings (UPDLOCK, HOLDLOCK) WHERE FLightID = @Id)
?Übergeben Sie Updlock-, Rowlock- und Holdlock-Hinweise, wenn Sie die Existenz der Zeile testen.
Der Updlock-Hinweis zwingt die Abfrage, eine Update-Sperre für die Zeile zu aktivieren, falls diese bereits vorhanden ist, und verhindert, dass andere Transaktionen sie ändern, bis Sie sie festschreiben oder zurücksetzen.
Der Holdlock-Hinweis erzwingt, dass die Abfrage eine Bereichssperre durchführt, wodurch verhindert wird, dass andere Transaktionen eine Zeile hinzufügen, die Ihren Filterkriterien entspricht, bis Sie ein Commit durchführen oder ein Rollback durchführen.
Der Rowlock-Hinweis erzwingt die Sperrgranularität auf Zeilenebene anstelle der Standard-Seitenebene, sodass Ihre Transaktion andere Transaktionen nicht blockiert, die versuchen, nicht verwandte Zeilen auf derselben Seite zu aktualisieren (beachten Sie jedoch den Kompromiss zwischen reduzierter Konkurrenz und der Zunahme von Overhead sperren - Sie sollten vermeiden, eine große Anzahl von Sperren auf Zeilenebene in einer einzigen Transaktion zu verwenden.
Weitere Informationen finden Sie unter http://msdn.microsoft.com/en-us/library/ms187373.aspx .
Beachten Sie, dass Sperren verwendet werden, wenn die Anweisungen ausgeführt werden, die sie ausführen. Wenn Sie begin tran aufrufen, erhalten Sie keine Immunität gegen eine andere Transaktion, die Sperren für etwas einklemmt, bevor Sie dazu gelangen. Sie sollten versuchen, Ihr SQL so zu faktorisieren, dass Sperren für die kürzestmögliche Zeit gehalten werden, indem Sie die Transaktion so schnell wie möglich festschreiben (spät erwerben, früh freigeben).
Beachten Sie, dass Sperren auf Zeilenebene möglicherweise weniger effektiv sind, wenn Ihre PK ein Bigint ist, da das interne Hashing auf SQL Server für 64-Bit-Werte entartet ist (verschiedene Schlüsselwerte können auf dieselbe Sperren-ID hashen).
quelle
exists
Überprüfung ohne Sperrhinweise umgangen werden.Ich schreibe meine Lösung. Meine Methode steht nicht für 'if' oder 'merge'. Meine Methode ist einfach.
Beispielsweise:
Erläuterung:
(1) SELECT col1, col2 FROM TableName WHERE col1 = @ par1 AND col2 = @ par2 Es wählt aus den gesuchten Werten von TableName aus
(2) SELECT @ par1, @ par2 WO NICHT EXISTIERT Es dauert, wenn es nicht aus (1) Unterabfrage existiert
(3) Fügt in TableName (2) Schrittwerte ein
quelle
Mit dem folgenden Modell konnte ich endlich eine Zeile einfügen, sofern sie noch nicht vorhanden war:
was ich gefunden habe bei:
http://www.postgresql.org/message-id/[email protected]
quelle
Das musste ich erst kürzlich tun:
quelle
Sie können die Zusammenführungsfunktion verwenden , um dies zu erreichen. Andernfalls können Sie Folgendes tun:
quelle
Die vollständige Lösung finden Sie weiter unten (einschließlich Cursorstruktur). Vielen Dank an Cassius Porcus für den
begin trans ... commit
Code aus dem obigen Beitrag.quelle
quelle
quelle
Der beste Ansatz für dieses Problem besteht darin, zuerst die Datenbankspalte EINZIGARTIG zu machen
ALTER TABLE table_name ADD UNIQUE KEY
THEN INSERT IGNORE INTO table_name
wird der Wert nicht eingefügt, wenn er zu einem doppelten Schlüssel führt / bereits in der Tabelle vorhanden ist.quelle