Frage

Ich habe eine Anwendung, die einfallenden Zahlen verwendet (neben anderen Arten von Zahlen). Diese Zahlen werden in einer Tabelle mit dem Namen „Number_Setup“ gespeichert, die den aktuellen Wert des Zählers enthält.

Wenn die App einen neuen Vorfall erzeugt, es number_setup Tisch und bekommt die erforderliche Anzahl Zähler Reihe (Zähler täglich zurückgesetzt werden kann, wöchentlich, usw. und werden als int die gespeichert). Es dann incremenets die Zähler und aktualisiert die Zeile mit dem neuen Wert.

Die Anwendung ist Multi-User (ca. 100 Anwender zu jeder Zeit sowie SQL-Jobs, die laufen und greifen 100 von Ereignisdatensätzen und Anfrage Vorfall Nummern für jeden). Der Vorfall Tabelle hat einige doppelte Vorfall Zahlen, wo sie nicht sein sollten Duplikat.

Eine gespeicherte Prozedur verwendet wird, um die nächsten Zähler abgerufen werden.

SELECT @Counter = counter, @ShareId=share_id, @Id=id
FROM Number_Setup
WHERE LinkTo_ID=@LinkToId
AND Counter_Type='I'

IF isnull(@ShareId,0) > 0
BEGIN 
    -- use parent counter
    SELECT @Counter = counter, @ID=id
    FROM Number_Setup
    WHERE Id=@ShareID
END

SELECT @NewCounter = @Counter + 1

UPDATE Number_Setup SET Counter = @NewCounter
WHERE id=@Id

Ich habe jetzt umgeben, dass der Block mit einer Transaktion, aber ich bin nicht ganz sicher, ‚es wird zu 100% fix das Problem, wie ich glaube, es gibt noch gemeinsame Sperren, so kann der Zähler sowieso gelesen werden.

Vielleicht kann ich überprüfen, ob der Zähler nicht aktualisiert worden ist, in der Update-Anweisung

UPDATE Number_Setup SET Counter = @NewCounter
WHERE Counter = @Counter
IF @@ERROR = 0 AND @@ROWCOUNT > 0 
    COMMIT TRANSACTION
ELSE
    ROLLBACK TRANSACTION

Ich bin sicher, dass dies ist ein häufiges Problem mit Rechnungsnummern in Finanz-Anwendungen usw.
Ich kann die Logik in Code entweder und auf dieser Ebene Verwendung Verriegelung. Ich habe auch bei HOLDLOCK gesperrt, aber ich bin nicht sicher, ob es die Anwendung. Sollte es auf den beiden SELECT-Anweisungen gesetzt werden?

Wie kann ich sicherstellen, keine Duplikate erstellt?

War es hilfreich?

Lösung

Der Trick ist, den Zähler-Update zu tun und in einer einzigen atomaren Operation lesen:

UPDATE Number_Setup SET Counter = Counter+1
OUTPUT INSERTED.Counter 
WHERE id=@Id;

Dies obwohl zuweisen nicht die neuen Zähler zu @NewCounter, sondern gibt ihm als Ergebnismenge an den Client. Wenn Sie es zuweisen müssen, verwenden Sie eine Zwischentabelle Variable für die Ausgabe der neuen Zähler INTO:

declare @NewCounter int;
declare @tabCounter table (NewCounter int);
UPDATE Number_Setup SET Counter = Counter+1
OUTPUT INSERTED.Counter INTO @tabCounter (NewCounter)
WHERE id=@Id
SELECT @NewCounter = NewCounter FROM @tabCounter;

Dies löst das Problem der Zähler machen erhöhen atomar. Sie haben noch andere Rennbedingungen im Verfahren, weil die LinkTo_Id und share_id kann noch nach der ersten Aktualisierung wählen, so dass Sie den Zähler der falschen Link zu erhöhen, können Artikel, aber das kann nicht nur aus diesem Codebeispiel gelöst werden, wie es auch abhängig auf dem Code, actualy aktualisiert die shared_id und / oder LinkTo_Id.

BTW, sollten Sie in die habbit Namen Ihrer Felder mit konsistentem Fall erhalten. Wenn sie sind genannt konsequent dann Sie müssen verwenden, um die genaue Übereinstimmung Fall in T-SQL-Code. Ihre Skripte laufen gut jetzt, nur weil Sie einen Fall insensitive Sortierung Server haben, wenn Sie auf einem Groß- und Kleinschreibung Sortierungsserver bereitstellen und Ihre Skripts nicht übereinstimmen den genauen Fall des Feldes / Tabellen-Namen Fehler in Hülle und Fülle folgen werden.

Andere Tipps

Sie haben versucht, GUIDs statt autoincrements als eindeutige Kennung verwenden?

Wenn Sie die ablity haben Ihren Job zu ändern, die mutiple Aufzeichnungen bekommt, würde ich das Denken ändern, so dass Ihr Zähler ist eine Identitätsspalte. Dann, wenn Sie den nächsten Datensatz bekommen Sie nur einen Einsatz machen und die @@ Identität der Tabelle erhalten. Das würde sicherstellen, dass Sie die größte Nummer. Sie würden auch eine dbccReseed tun müssen, um den Zähler anstatt nur die Aktualisierung der Tabelle zurückgesetzt werden, wenn Sie die Identität zurücksetzen möchten. Das einzige Problem ist, dass Sie 100 oder so Einsätze als Teil Ihres SQL-Jobs tun würden haben eine Gruppe von Identitäten zu erhalten. Das kann zu viel Aufwand, aber eine Identität Spalte ist ein garantierter Weg, eindeutige Zahlen zu erhalten.

Ich könnte etwas fehlen, aber es scheint, wie Sie versuchen, Technologie neu zu erfinden, die bereits von den meisten Datenbanken gelöst wurde.

anstelle das Lesen und Aktualisierung von der Spalte ‚Zähler‘ in der Number_Setup Tabelle, warum Sie nicht nur einen selbstinkrementierende Primärschlüssel für Ihren Zähler verwenden? Sie werden nie einen doppelten Wert für einen Primärschlüssel haben.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top