2009-07-24 6 views
2

ich eine Tabelle 'foo' bekam die Postgres: Wie INSERT in einem speziellen Fall zu verhindern

ID | NAME 
------+---------------------------- 
    123 | PiratesAreCool 
    254 | NinjasAreCoolerThanPirates 

und eine zweite Tabelle 'bar'

SID | ID | created | dropped 
------+------+------------+----------- 
    9871 | 123 | 03.24.2009 | 03.26.2009 
    9872 | 123 | 04.02.2009 | 

bar.ID ist eine Referenz (ausländische wie

aussieht Schlüssel) zu foo.ID.

Jetzt möchte ich verhindern, dass Sie einen neuen Datensatz in 'bar' einfügen können, wenn es einen Datensatz mit der gleichen ID gibt und bar.dropped ist Null in diesem Datensatz.

Also, wenn die 'Bar' sieht aus wie oben

INSERT INTO BAR VALUES ('9873','123','07.24.2009',NULL); 

sollte verboten werden, aber

INSERT INTO BAR VALUES ('9873','254','07.24.2009',NULL); 

sollte erlaubt werden (weil es keine 'offenen' Bar-Rekord für ‚NinjasAreCoolerThanPirates ist ').

Wie mache ich das? Ich hoffe mein Problem ist klar und jemand kann mir helfen.

Antwort

3

hmm, das sollte ausreichen, um nur einen eindeutigen Index zu erstellen.

create unique index ix_open_bar on bar (id, dropped); 

natürlich, dass auch die Wirkung haben würde, dass Sie nicht über eine Bar zweimal pro Tag fallen kann (es sei denn, die einen Zeitstempel fallen gelassen ist, die das Risiko minimieren würde)

Eigentlich bemerkte ich, dass Postgres haben Unterstützung für Teilindizes:

create unique index ix_open_bar on bar (id) where dropped is null; 

Update: nach einigen Tests wird die eindeutige Einschränkung auf Nullwerte nicht erzwungen, sondern die Teilindizes wird immer noch funktionieren.

Und wenn Sie nicht über die Teilindizes verwenden möchten, könnte dies auch funktionieren:

create unique index ix_open_bar on bar(id, coalesce(dropped, 'NULL')); 

jedoch bei der Verwendung von coalesce, müssen Sie auf ihnen die gleichen Datentypen haben (so fallen gelassen, wenn ist ein Zeitstempel, müssen Sie stattdessen "NULL" in einen Zeitstempelwert ändern.

+0

Ein eindeutiger Index auf eine Null-Spalte wird auf diese Weise nicht funktionieren. – Draemon

+0

True, aktualisierte die Antwort. Frage mich, ob das nur ein Postgres-Ding ist, ich erinnere mich, dass es an anderen Orten funktioniert hat. –

+0

OK, nach einem kleinen Test "create unique index ix_open_bar in bar (id) wo dropped ist null;" scheint die beste Lösung zu sein. Vielen Dank. – sloth

3

Dies wird nur einen Datensatz einfügen, wenn es keine ‚offenen‘ Rekord in bar für Ihre ID

ist
INSERT INTO bar 
SELECT '9873','254','07.24.2009',NULL 
WHERE NOT EXISTS(SELECT 1 FROM bar WHERE ID='254' AND dropped IS NULL) 
+0

hat das nicht für dich funktioniert? –

+0

Dies ist viel weniger effizient als eine Beschränkung auf das Feld. Eine Einschränkung kann vom Datenbankserver für den speziellen Fall optimiert werden, während Ihre Abfrage für eine allgemeine Bereichssuche optimiert werden muss. Wenn Sie keinen Index haben, wird er monströs langsam sein, und wenn Sie dies tun, können Sie den Index auch dazu bringen, eindeutige Einträge zu akzeptieren. Dies ist keine gute Lösung. – Christopher

+0

Das ist das Verhalten, nach dem ich suche, aber gibt es eine Möglichkeit, dies als Einschränkung zu regeln, so dass Sie nicht versehentlich einen zweiten Datensatz mit droped = null schreiben können? – sloth

2

einen Trigger auf dem Tisch bar auf Einsatz aufstellen, die, wenn die aktuelle Zeile überprüft, Die ID ist bereits in der Tabelle vorhanden und wird abgelehnt.

Ich weiß nicht, die spezifische Postgres Syntax, aber es sollte in etwa so funktionieren:

CREATE TRIGGER trigger_name BEFORE INSERT ON bar 
IF EXISTS (
    SELECT 1 
    FROM bar 
    WHERE bar.ID = inserted.ID 
    AND bar.dropped IS NULL 
) 
BEGIN 
    // raise an error or reject or whatever Postgres calls it. 
END 

Und dann, wenn Sie versuchen, in bar einzufügen, wird dieser Trigger überprüfen, ob etwas bereits existiert, und es ablehnen wenn ja. Wenn bar.dropped nicht null ist, wird die Einfügung problemlos möglich.

Wenn jemand die richtige Syntax dafür kennt, können Sie meine Antwort bearbeiten.

+0

danke, das ist, was ich gesucht habe – sloth

+2

Trigger für das ist Overkill. –

2

Sie können einen partiellen Index mit einer WHERE-Klausel erstellen. Für Ihre Zwecke könnte dies tun;

CREATE UNIQUE INDEX my_check on bar(id) where dropped is null; 

id Unter der Annahme, 124 nicht in der Tabelle existiert nicht, das erlaubt werden, aber nur ein Datensatz kann = für eine bestimmte ID NULL fallen gelassen haben:

INSERT INTO BAR VALUES ('9873','124','07.24.2009',NULL); 

Und das wird, ob erlaubt werden oder nicht 124 ist bereits vorhanden

INSERT INTO BAR VALUES ('9873','124','07.24.2009','07.24.2009'); 

Wenn 125 bereits vorhanden ist, wird dies nicht

INSERT INTO BAR VALUES ('9873','125','07.24.2009',NULL); 
allowd werden

aber das wird

INSERT INTO BAR VALUES ('9873','125','07.24.2009','07.24.2009'); 
Verwandte Themen