Ein Index kann nicht zurückgestellt werden - es spielt keine Rolle, ob es sich um eine UNIQUE
Teil- oder eine UNIQUE
Nebenbedingung handelt oder nicht . Andere Arten von Beschränkungen ( FOREIGN KEY
, PRIMARY KEY
, EXCLUDE
) sind auch aufschiebbare - aber nicht CHECK
Zwänge.
Daher wird der eindeutige Teilindex (und die implizite Einschränkung, die er implementiert) bei jeder Anweisung (und tatsächlich nach jeder Zeileneinfügung / -aktualisierung in der aktuellen Implementierung) und nicht am Ende der Transaktion überprüft.
Wenn Sie diese Einschränkung als verzögernd implementieren möchten, können Sie dem Entwurf eine weitere Tabelle hinzufügen. Etwas wie das:
CREATE TABLE public.booking_status
( booking_id int NOT NULL, -- same types
check_in timestamp NOT NULL, -- as in
check_out timestamp NOT NULL, -- booking
CONSTRAINT unique_booking
UNIQUE (check_in, check_out)
DEFERRABLE INITIALLY DEFERRED,
CONSTRAINT unique_booking_fk
FOREIGN KEY (booking_id, check_in, check_out)
REFERENCES public.booking (booking_id, check_in, check_out)
DEFERRABLE INITIALLY DEFERRED
) ;
Bei diesem Entwurf und unter der Annahme, dass booking_status
nur 2 Optionen (0 und 1) möglich sind, können Sie ihn vollständig entfernen booking
(wenn sich eine Zeile auf befindet booking_status
, ist es 1, wenn nicht 0).
Eine andere Möglichkeit wäre, eine EXCLUDE
Einschränkung (ab) zu verwenden :
ALTER TABLE booking
ADD CONSTRAINT unique_booking
EXCLUDE
( check_in WITH =,
check_out WITH =,
(CASE WHEN booking_status = 1 THEN TRUE END) WITH =
)
DEFERRABLE INITIALLY DEFERRED ;
Getestet bei dbfiddle .
Was das oben Genannte bewirkt:
Der CASE
Ausdruck wird, NULL
wenn booking_status
null oder anders als 1 ist. Wir könnten schreiben, (CASE WHEN booking_status = 1 THEN TRUE END)
als (booking_status = 1 OR NULL)
ob das alles klarer machen würde.
Eindeutige und ausschließende Bedingungen akzeptieren Zeilen, in denen einer oder mehrere der Ausdrücke NULL sind. Es wirkt also als gefilterter Index mit WHERE booking_status = 1
.
Alle WITH
Operatoren sind =
so, dass es als UNIQUE
Einschränkung wirkt .
Diese beiden zusammen bewirken, dass die Einschränkung als gefilterter eindeutiger Index fungiert.
Aber es ist eine Einschränkung und EXCLUDE
Einschränkungen können aufgeschoben werden.
(CASE WHEN booking_status = 1 THEN TRUE END) WITH =)
sollte durch ersetzt werden,) WHERE (booking_status = 1)
da "Ausschlussbeschränkungen mithilfe eines Index implementiert werden" und dieser Teilindex mitWHERE
kleiner und schneller ist - postgresql.org/docs/current/sql-createtable.html und postgresql.org/docs/current/sql- createindex.htmlObwohl die Jahre dieser Frage vergangen sind, möchte ich für spanischsprachige Personen klarstellen, dass die Tests in Postgres durchgeführt wurden:
Die folgende Einschränkung wurde zu einer Tabelle mit 1337 Datensätzen hinzugefügt, wobei das Kit der Primärschlüssel ist:
Dies erstellt einen Standardprimärschlüssel NOT DEFERRED für die Tabelle. Wenn Sie also das nächste UPDATE versuchen, erhalten Sie folgende Fehlermeldung:
In Postgres wird durch Ausführen eines UPDATE für jede Zeile überprüft, ob die Einschränkung oder Einschränkung erfüllt ist.
Das CONSTRAINT IMMEDIATE wird nun erstellt und jede Anweisung wird separat ausgeführt:
Hier ermöglicht SI das Ändern des Primärschlüssels, da er den gesamten ersten vollständigen Satz (1328 Zeilen) ausführt; Obwohl es sich in der Transaktion (BEGIN) befindet, wird der CONSTRAINT sofort nach Beendigung jedes Satzes validiert, ohne COMMIT ausgeführt zu haben. Daher wird beim Ausführen des INSERT ein Fehler generiert. Zum Schluss haben wir den CONSTRAINT DEFERRED erstellt und machen folgendes:
Wenn wir jede Anweisung von ** Block 2 ** und jeden Satz einzeln ausführen, wird kein Fehler für das INSERT generiert, da es nicht validiert, sondern das endgültige COMMIT ausgeführt wird, wenn es eine Inkonsistenz feststellt.
Für vollständige Informationen in Englisch schlage ich vor, dass Sie die Links überprüfen:
Deferrable SQL-Einschränkungen im Detail
NOT DEFERRABLE versus DEFERRABLE INITIALLY SOFORT
quelle