Postgresql: restricción condicionalmente única

116

Me gustaría agregar una restricción que imponga la unicidad en una columna solo en una parte de una tabla.

ALTER TABLE stop ADD CONSTRAINT myc UNIQUE (col_a) WHERE (col_b is null);

La WHEREparte de arriba es una ilusión.

¿Alguna forma de hacer esto? ¿O debería volver al tablero de dibujo relacional?

EoghanM
fuente
2
Comúnmente hecho. Ver "índice único parcial"
Craig Ringer
11
@yvesonline no, esa es una restricción única regular. El cartel quiere una restricción única parcial .
Craig Ringer

Respuestas:

186

PostgreSQL no define una UNIQUErestricción parcial (es decir, condicional) ; sin embargo, puede crear un índice único parcial . PostgreSQL usa índices únicos para implementar restricciones únicas, por lo que el efecto es el mismo, simplemente no verá la restricción en la lista information_schema.

CREATE UNIQUE INDEX stop_myc ON stop (col_a) WHERE (col_b is NOT null);

Ver índices parciales .

Craig Ringer
fuente
24
¡Súper! No es intuitivo que la "restricción" no se muestre como una restricción, pero sin embargo da el error deseado deERROR: duplicate key value violates unique constraint "stop_myc"
EoghanM
7
Vale la pena señalar que esto no permitirá crear FK que hagan referencia a ese campo parcialmente único.
ffflabs
11
También vale la pena señalar que los efectos de este índice no se pueden diferir. Si necesita realizar actualizaciones masivas, esto puede presentar un problema ya que la unicidad se verifica después de cada fila, no después de la declaración como lo sería para una restricción o después de la transacción como sería para una restricción diferible.
sage88
37

ya se ha dicho que PG no define una restricción ÚNICA parcial (es decir, condicional). Además, la documentación dice que la forma preferida de agregar una restricción única a una tabla son los ADD CONSTRAINT índices únicos

La forma preferida de agregar una restricción única a una tabla es ALTER TABLE ... ADD CONSTRAINT. El uso de índices para hacer cumplir restricciones únicas podría considerarse un detalle de implementación al que no se debe acceder directamente. Sin embargo, se debe tener en cuenta que no es necesario crear índices manualmente en columnas únicas; hacerlo simplemente duplicaría el índice creado automáticamente.

Hay una manera de implementarlo usando restricciones de exclusión (gracias a @dukelion por esta solución)

En tu caso se verá como

ALTER TABLE stop ADD CONSTRAINT myc EXCLUDE (col_a WITH =) WHERE (col_b IS null);
Peter Yeremenko
fuente
en ese enfoque, no usa "using" para definir el método de índice, por lo que puede ser extremadamente lento o postgres crea un índice predeterminado en eso. Ese método es la opción canónica, ¡pero nunca la mejor opción! Creo que necesitará una cláusula de "uso" con índice para que esa elección sea la mejor.
Natan Medeiros
10
Si bien es más lenta, la ventaja de la solución de exclusión es que se puede aplazar (y de forma predeterminada se aplaza hasta el final de la declaración). Por el contrario, la solución de índice única aceptada no se puede aplazar (y se comprueba después de cada cambio de fila). Por lo tanto, una actualización masiva a menudo no es posible porque los pasos durante la actualización violarían la restricción única, incluso si no se violaría al final de la declaración de actualización atómica.
sage88
1
Esta nota se eliminó de los documentos en agosto de 2015
Raniz