Tengo una columna: standard BOOLEAN NOT NULL
Me gustaría imponer una fila Verdadero y todas las demás Falso. No hay FK ni nada más dependiendo de esta restricción. Sé que puedo lograrlo con plpgsql, pero esto parece un mazo. Preferiría algo como a CHECK
o UNIQUE
restricción. Cuanto más simple, mejor.
Una fila debe ser verdadera, no todas pueden ser falsas (por lo que la primera fila insertada debería ser verdadera).
La fila deberá actualizarse, lo que significa que tengo que esperar para verificar las restricciones hasta que se realicen las actualizaciones, ya que todas las filas se pueden configurar como False primero y una fila True después.
Hay un FK entre products.tax_rate_id
y tax_rate.id
, pero no tiene nada que ver con la tasa impositiva predeterminada o estándar, que puede seleccionar el usuario para facilitar la creación de nuevos productos.
PostgreSQL 9.5 si es importante.
Antecedentes
La tabla es la tasa de impuestos. Una de las tasas impositivas es la predeterminada ( standard
ya que la predeterminada es un comando de Postgres). Cuando se agrega un nuevo producto, la tasa impositiva estándar se aplica al producto. Si no hay standard
, la base de datos debe hacer una conjetura o todo tipo de comprobaciones innecesarias. La solución simple, pensé, era asegurarme de que haya una standard
.
Por "predeterminado" arriba, me refiero a la capa de presentación (IU). Hay una opción de usuario para cambiar la tasa impositiva predeterminada. Necesito agregar controles adicionales para asegurar que la GUI / usuario no intente establecer tax_rate_id en NULL, o simplemente establecer una tasa impositiva predeterminada.
Respuestas:
Variante 1
Como todo lo que necesita es una sola columna
standard = true
, establezca el estándar en NULL en todas las demás filas. Entonces funciona unaUNIQUE
restricción simple , ya que los valores NULL no la violan:DEFAULT
es un recordatorio opcional de que la primera fila ingresada debe convertirse en la predeterminada. No está haciendo cumplir nada. Si bien no puede establecer más de una fila enstandard = true
, aún puede establecer todas las filas como NULL. No hay una forma limpia de evitar esto con solo restricciones en una sola tabla.CHECK
Las restricciones no consideran otras filas (sin trucos sucios).Relacionado:
Restrinja la existencia de dos valores de columna específicos al mismo tiempo
Agregar restricción para hacer que la columna sea única por grupo de filas
Actualizar:
Para permitir un comando como (donde la restricción solo se cumple al final de la declaración):
.. la
UNIQUE
restricción tendría que serDEFERRABLE
. Ver:dbfiddle aquí
Variante 2
Tenga una segunda tabla con una sola fila como:
Crea esto como superusuario:
Ahora siempre hay una única fila que apunta al estándar (en este caso simple que también representa la tasa estándar directamente). Solo un superusuario podría romperlo. También puede rechazar eso con un disparador
BEFORE DELETE
.dbfiddle aquí
Relacionado:
Puede agregar a
VIEW
para ver lo mismo que en la variante 1 :En consultas donde lo único que desea es la tarifa estándar, use (solo)
taxrate_standard.taxrate
directamente.Luego agregaste:
Una aplicación del pobre hombre de la variante 2 sería simplemente añadir una fila a
products
(o cualquier tabla similar) señalando el tipo general de gravamen; un producto ficticio que podría llamar "tasa impositiva estándar", si su configuración lo permite.Las restricciones de FK hacen cumplir la integridad referencial. Para completarlo, aplique
tax_rate_id IS NOT NULL
la fila (si ese no es el caso de la columna en general). Y no permitir su eliminación. Ambos podrían ser puestos en disparadores. No hay mesa extra, pero menos elegante y no tan confiable.fuente
CROSS JOIN
comparar el estándar,LEFT JOIN
el específico y luegoCOALESCE
entre los dos.CONSTRAINT standard_only_1_true UNIQUE (standard)
: Supongo que la tabla no será grande, por lo que no importa mucho, pero dado que la restricción definirá un índice en toda la tabla, ¿no sería un índice único parcial conWHERE (standard)
menos espacio?Puede usar un índice filtrado
dbfiddle aquí
Pero como dijiste, la primera fila debe ser verdadera, entonces podrías usar una restricción CHECK, pero incluso usando una función puedes eliminar la primera fila más tarde.
dbfiddle aquí
Puede resolverlo agregando un disparador ANTES DE BORRAR para asegurarse de que la primera fila (foo es verdadera) nunca se elimine.
dbfiddle aquí
fuente