Ich habe eine Spalte: standard BOOLEAN NOT NULL
Ich möchte eine Zeile True und alle anderen False erzwingen. Abhängig von dieser Einschränkung gibt es keine FKs oder sonst etwas. Ich weiß, dass ich es mit plpgsql schaffen kann, aber das scheint ein Vorschlaghammer zu sein. Ich würde so etwas wie eine CHECK
oder UNIQUE
Einschränkung bevorzugen . Je einfacher desto besser.
Eine Zeile muss True sein, sie kann nicht alle False sein (daher müsste die erste eingefügte Zeile True sein).
Die Zeile muss aktualisiert werden, was bedeutet, dass ich warten muss, um die Einschränkungen zu überprüfen, bis die Aktualisierungen abgeschlossen sind, da alle Zeilen zuerst auf False und danach auf eine Zeile True gesetzt werden können.
Es gibt eine FK zwischen products.tax_rate_id
und tax_rate.id
, die jedoch nichts mit dem Standard- oder Standardsteuersatz zu tun hat, der vom Benutzer ausgewählt werden kann, um das Erstellen neuer Produkte zu vereinfachen.
PostgreSQL 9.5, wenn es darauf ankommt.
Hintergrund
Die Tabelle ist der Steuersatz. Einer der Steuersätze ist der Standard ( standard
da der Standard ein Postgres-Befehl ist). Wenn ein neues Produkt hinzugefügt wird, wird der Standardsteuersatz auf das Produkt angewendet. Wenn dies nicht standard
der Fall ist, muss die Datenbank entweder eine Vermutung anstellen oder alle Arten von nicht benötigten Überprüfungen durchführen. Die einfache Lösung, dachte ich, bestand darin, sicherzustellen, dass es eine gibt standard
.
Mit "Standard" oben meine ich für die Präsentationsschicht (UI). Es gibt eine Benutzeroption zum Ändern des Standardsteuersatzes. Ich muss entweder zusätzliche Überprüfungen hinzufügen, um sicherzustellen, dass die GUI / der Benutzer nicht versucht, die tax_rate_id auf NULL zu setzen, oder dann einfach einen Standardsteuersatz festlegen.
Antworten:
Variante 1
Da Sie nur eine einzelne Spalte mit benötigen
standard = true
, setzen Sie den Standard in allen anderen Zeilen auf NULL. DannUNIQUE
funktioniert eine einfache Einschränkung, da NULL-Werte sie nicht verletzen:DEFAULT
ist eine optionale Erinnerung daran, dass die erste eingegebene Zeile die Standardeinstellung sein sollte. Es erzwingt nichts. Obwohl Sie nicht mehr als eine Zeilestandard = true
festlegen können, können Sie dennoch alle Zeilen auf NULL setzen. Es gibt keine saubere Möglichkeit, dies mit nur Einschränkungen in einer einzelnen Tabelle zu verhindern .CHECK
Einschränkungen berücksichtigen keine anderen Zeilen (ohne schmutzige Tricks).Verbunden:
Beschränken Sie, dass zwei bestimmte Spaltenwerte gleichzeitig vorhanden sind
Fügen Sie eine Einschränkung hinzu, um die Spalte pro Zeilengruppe eindeutig zu machen
Aktualisieren:
So erlauben Sie einen Befehl wie (wobei die Einschränkung nur am Ende der Anweisung erfüllt ist):
.. die
UNIQUE
Einschränkung müsste seinDEFERRABLE
. Sehen:dbfiddle hier
Variante 2
Habe einen zweiten Tisch mit einer einzelnen Zeile wie:
Erstellen Sie dies als Superuser:
Jetzt gibt es immer eine einzelne Zeile, die auf den Standard zeigt (in diesem einfachen Fall wird auch die Standardrate direkt dargestellt). Nur ein Superuser konnte es brechen. Sie können dies auch mit einem Auslöser nicht zulassen
BEFORE DELETE
.dbfiddle hier
Verbunden:
Sie können ein hinzufügen
VIEW
, um dasselbe wie in Variante 1 zu sehen :Verwenden Sie bei Abfragen, bei denen Sie nur den Standardtarif wünschen, (nur)
taxrate_standard.taxrate
direkt.Sie haben später hinzugefügt:
Die Implementierung von Variante 2 durch einen armen Mann würde darin bestehen, nur eine Zeile
products
(oder eine ähnliche Tabelle) hinzuzufügen, die auf den Standardsteuersatz verweist. Ein Dummy-Produkt, das Sie möglicherweise als "Standardsteuersatz" bezeichnen - sofern Ihr Setup dies zulässt.Die FK-Einschränkungen erzwingen die referenzielle Integrität. Um dies zu vervollständigen, erzwingen Sie
tax_rate_id IS NOT NULL
für die Zeile (falls dies für die Spalte im Allgemeinen nicht der Fall ist). Und verbiete seine Löschung. Beides könnte ausgelöst werden. Kein zusätzlicher Tisch, aber weniger elegant und nicht so zuverlässig.quelle
CROSS JOIN
gegen den Standard,LEFT JOIN
gegen den spezifischen und dannCOALESCE
zwischen den beiden vorgeht.CONSTRAINT standard_only_1_true UNIQUE (standard)
: Ich nehme an, die Tabelle wird nicht groß sein, also spielt es keine Rolle, aber da die Einschränkung einen Index für die gesamte Tabelle definiert, würde ein teilweise eindeutiger Index nichtWHERE (standard)
weniger Speicherplatz verbrauchen?Sie können einen gefilterten Index verwenden
dbfiddle hier
Aber wie Sie sagten, muss die erste Zeile wahr sein, dann können Sie eine CHECK-Einschränkung verwenden, aber selbst mit einer Funktion können Sie die erste Zeile später löschen.
dbfiddle hier
Sie können das Problem lösen, indem Sie einen BEFORE DELETE-Trigger hinzufügen, um sicherzustellen, dass die erste Zeile (foo ist wahr) niemals gelöscht wird.
dbfiddle hier
quelle