2016-07-25 14 views
2

Ich muss Eindeutigkeit für eine Spalte erzwingen, aber nur wenn andere Spalte wahr ist. Zum Beispiel:Conditinal Unique Constraint nicht korrekt aktualisiert

create temporary table test(id serial primary key, property character varying(50), value boolean); 
insert into test(property, value) values ('a', false); 
insert into test(property, value) values ('a', true); 
insert into test(property, value) values ('a', false); 

Und ich erzwingen die Einzigartigkeit mit einem bedingten Index:

create unique index on test(property) where value = true; 

So weit so gut, das Problem entsteht, wenn ich versuche, die Zeile zu ändern, die den Wert auf true gesetzt hat. Es funktioniert, wenn ich tun:

update test set value = new_value from (select id, id=3 as new_value from test where property = 'a')new_test where test.id = new_test.id 

Aber es funktioniert nicht, wenn ich tun:

update test set value = new_value from (select id, id=1 as new_value from test where property = 'a')new_test where test.id = new_test.id 

Und ich bekomme:

ERROR: duplicate key value violates unique constraint "test_property_idx" 
DETAIL: Key (property)=(a) already exists. 

********** Error ********** 

ERROR: duplicate key value violates unique constraint "test_property_idx" 
SQL state: 23505 
Detail: Key (property)=(a) already exists. 

Im Grunde funktioniert es, wenn die Zeile mit Wert true ein Primärschlüssel mit einem größeren Wert als die aktuelle Zeile, die truthy ist. Irgendeine Idee, wie man es umgehen kann?

Natürlich könnte ich tun:

update test set value = false where property='a'; 
update test set value = true where property = 'a' and id = 1; 

Allerdings bin ich diese Anfragen von Knoten ausgeführt wird, und es ist vorzuziehen, nur eine Abfrage auszuführen.

Ich bin mit Postgres 9.5

+0

Versuchen Sie, die eindeutige Einschränkung zu machen, die ursprünglich aufgehoben wurde. Nicht sicher für die Syntax leider. –

+0

Scheint eigentlich, dass es mit UNIQUE INDEX nicht möglich ist, nur mit CONSTRAINT UNIQUE –

+1

Verwenden Sie gespeicherte Funktion, um die Daten zu aktualisieren. 'update test set value = false wo property = 'a' und value;' wäre effizienter. BTW 'update test set Wert = id = 3 wo Eigenschaft = 'a';' ist einfacher. – Abelisto

Antwort

3

Ihr Problem ist, dass UPDATE statements cannot have an ORDER BY clause in SQL (in einigen RDBMS haben kann, aber nicht in PostgreSQL).

Die übliche Lösung besteht darin, die Beschränkung aufschiebbar zu machen. Aber Sie verwenden einen teilweise eindeutigen Index & Indizes können nicht als aufschiebbar deklariert werden.

Verwenden Sie stattdessen eine exclusion constraint: Sie sind die Verallgemeinerung von eindeutigen Einschränkungen & kann teilweise auch sein.

ALTER TABLE test 
    ADD EXCLUDE (property WITH =) WHERE (value = true) 
    DEFERRABLE INITIALLY DEFERRED; 
Verwandte Themen