2014-11-27 5 views
5

Überprüfen dieses einfache Stück Code, das einen Generator zu schaffen eindeutige Primärschlüssel in einem Firebird Tabelle verwendet:Wie verschwende ich nicht Generator-Werte, wenn Sie sie Serverseite mit Firebird verwenden?

CREATE OR ALTER TRIGGER ON_BEFOREINSERT_PK_BOOKING_ITEM FOR BOOKING_ITEM BEFORE INSERT POSITION 0 
    AS 
    BEGIN 
    IF ((NEW.booking_item_id IS NULL) OR (NEW.booking_item_id = 0)) THEN BEGIN 
     SELECT GEN_ID(LastIdBookingItem, 1) FROM RDB$DATABASE INTO :NEW.booking_item_id; 
    END 
    END! 

Dieser Trigger Greifern und erhöht dann einen erzeugten Wert zuordnet somit für die Buchung Item ID Schaffung ein automatisch inkrementierten Schlüssel für die Tabelle BOOKING_ITEM. Der Trigger überprüft sogar, dass der Buchungs-ID noch kein Wert zugewiesen wurde.

Das Problem ist, dass der automatisch inkrementierte Wert verloren geht (verschwendet wird), wenn der BOOKING_ITEM-Datensatz aus irgendeinem Grund nicht gebucht werden kann.

Ich habe ein paar Ideen, wie man diese Verschwendung vermeiden kann, aber Bedenken über jeden einzelnen haben. Hier sind sie:

  1. Decrement der Zähler, wenn ein Buchungsfehler auftritt. Innerhalb des Triggers richte ich einen try-except-Block ein (try-excess-Blöcke existieren sogar in Firebird PSQL?) Und führe einen SELECT GEN_ID(LastIdBookingItem, -1) FROM RDB$DATABASE für Post-Exceptions aus. Würde das funktionieren? Was passiert, wenn eine andere Transaktion hereinkommt und den Generator inkrementiert, bevor ich ihn dekrementiere? Das würde wirklich alles durcheinander bringen.

  2. Verwenden Sie eine temporäre ID. Setzen Sie die ID auf einen eindeutigen Temperaturwert, den ich beim Trigger AFTER INSERT auf den Generatorwert ändern möchte. Diese Methode fühlt sich etwas künstlich an und erfordert einen Weg, der sicherstellt, dass die Temp-ID eindeutig ist. Aber was, wenn die booking_item_id clientseitig bereitgestellt wurde, wie würde ich das von einer temporären ID unterscheiden? Plus ich brauche einen anderen Auslöser

  3. Verwenden Sie Transaktionssteuerung. Dies ist wie Option 1. Mit Ausnahme, dass ich den try-except-Block verwende, um den Generator zurückzusetzen, starte ich eine Transaktion und rolle sie dann zurück, wenn der Datensatz nicht posten kann. Ich kenne die Syntax für die Verwendung der Transaktionskontrolle nicht. Ich dachte, ich hätte irgendwo gelesen, dass SAVEPOINT/SET TRANSACTION in PSQL nicht erlaubt ist. Außerdem müsste der Rollback im AFTER INSERT-Trigger stattfinden, damit ich erneut einen Trigger brauche.

Sicherlich ist dies ein Problem für jeden Firebird-Entwickler, die Generatoren verwenden möchte. Irgendwelche anderen Ideen? Gibt es etwas, das mir fehlt?

+2

Die Antwort ist die gleiche wie für alle anderen DBMS, die Sequenzen verwenden: Mach dir keine Sorgen. Sequenzen sind nicht lückenlos und ihr Wert ist bedeutungslos. Ihr einziger Zweck ist es, als künstlicher Primärschlüssel zu dienen. Nichts mehr. Wenn Ihre Anwendung lückenlose Werte verwendet, können Sie einfach keine Sequenz verwenden (durchsuchen Sie diese Site nach "Lückenlose Sequenz" für Alternativen). –

+0

Ich suche diese Seite nach "Lückenlose Sequenz" für Alternativen. Vielen Dank. Meistens hast du Recht "Wen kümmert es", aber für einige Tabellen erzeuge ich eine Sequenznummer und verwende sie als, sagen wir, eine Rechnungsnummer. Gelegentlich bitten uns unsere Wirtschaftsprüfer, Rechnungen X bis X + n zu erstellen. Sicherlich müssen nicht alle Rechnungen aktiv sein, einige können storniert werden, aber sie fragen sich, ob irgendwelche Nummern fehlen. Sie stellen Fragen wie "Wo ist die Rechnung 12345?" –

+0

Richtig, für Rechnungsnummern benötigen Sie etwas anderes. Eine Sequenz ist dafür nicht geeignet. –

Antwort

8

Sequenzen sind außerhalb Transaktionssteuerung und mit ihnen einmischen Zahlen nur Probleme verursachen wird ‚Gapless‘ zu erhalten, weil eine andere Transaktion als auch gleichzeitig die Sequenz erhöhen könnte, um Lücken + Duplikate führen statt ohne Lücken:

  • startet: Generatorwert = 1
  • T1: Schritt: Wert 2 ist
  • T2: Schritt: Wert 3
  • T1: 'Rollback', Dekrement: Wert 2 (und nicht mehr als 1, wie zu erwarten)
  • T3: Schritt: Wert 3 => doppelte Wert

Sequenzen sollen in erster Linie zur Erzeugung von künstlichen Primärschlüssel verwendet werden, und Sie sollten über die Existenz von Lücken nicht kümmern: Es spielt keine Rolle, solange Die Nummer identifiziert den Datensatz eindeutig.

Wenn Sie eine auditierbare Zahlenfolge benötigen und keine Lücken vorhanden sind, sollten Sie keine Datenbanksequenz zum Generieren verwenden. Sie können eine Sequenz verwenden, um Nummern nach dem Erstellen und Übernehmen der Rechnung selbst zuzuordnen (damit sichergestellt ist, dass sie beibehalten wird). Eine Rechnung ohne Nummer ist noch nicht endgültig. Aber auch hier gibt es ein Fenster der Möglichkeit, eine Lücke zu bekommen, zB wenn ein Fehler oder ein anderer Fehler zwischen dem Zuweisen der Rechnungsnummer und dem Begehen auftritt.

Eine andere Möglichkeit besteht darin, explizit eine Nullrechnung (markiert als storniert/verlorene Nummer) mit den Lückennummern zu erstellen, damit der Auditor weiß, was mit dieser Rechnung passiert ist.

Abhängig von den lokalen Gesetzen und Vorschriften sollten Sie keine verlorenen Nummern "wiederverwerten", da dies als Betrug interpretiert werden könnte.

Sie können andere Ideen in "An Auditable Series of Numbers" finden. Dies beinhaltet auch ein Delphi-Projekt, das IBObjects verwendet, aber das Dokument selbst beschreibt das Problem und mögliche Lösungen recht gut.

Verwandte Themen