Por ejemplo, con una tabla similar a esta:
create table foo(bar int identity, chk char(1) check (chk in('Y', 'N')));
No importa si la bandera se implementa como a char(1)
, a bit
o lo que sea. Solo quiero poder imponer la restricción de que solo se puede establecer en una sola fila.
constraint
database-agnostic
referential-integrity
Jack Douglas
fuente
fuente
Respuestas:
SQL Server 2008: índice único filtrado
fuente
SQL Server 2000, 2005:
Puede aprovechar el hecho de que solo se permite un valor nulo en un índice único:
para 2000, puede que necesite
SET ARITHABORT ON
(gracias a @gbn por esta información)fuente
Oráculo:
Dado que Oracle no indexa las entradas donde todas las columnas indexadas son nulas, puede usar un índice único basado en funciones:
Este índice solo indexará una sola fila como máximo.
Conociendo este hecho del índice, también puede implementar la columna de bits de manera ligeramente diferente:
Aquí los valores posibles para la columna
chk
seránY
yNULL
. Solo una fila como máximo puede tener el valorY.
fuente
not null
restricción?not null
restricción si no desea valores nulos (no me quedó claro a partir de las especificaciones de la pregunta). Solo una fila puede tener el valor 'Y' en cualquier caso.default
)?Y
onull
, vea mi actualización.null
se omiten, a costa de cierta claridad, quizásCreo que este es un caso de estructurar sus tablas de base de datos correctamente. Para hacerlo más concreto, si tiene una persona con varias direcciones y desea que una sea la predeterminada, creo que debe almacenar el addressID de la dirección predeterminada en la tabla de personas, no tener una columna predeterminada en la tabla de direcciones:
Puede hacer que el DefaultAddressID sea anulable, pero de esta manera la estructura aplica su restricción.
fuente
MySQL:
Comprobación se ignora en MySQL por lo que tenemos que considerar
null
ofalse
como falso ytrue
lo verdadero. Como máximo 1 fila puede tenerchk=true
Puede considerar una mejora agregar un disparador para cambiar
false
altrue
insertar / actualizar como una solución alternativa por la falta de una restricción de verificación. Sin embargo, en mi opinión, no es una mejora.Esperaba poder usar un char (0) porque
Desafortunadamente, con MyISAM e InnoDB al menos, obtengo
--editar
después de todo, esta no es una buena solución, ya que en MySQL
boolean
es sinónimo detinyint(1)
, y por lo tanto, permite valores no nulos de 0 o 1. Es posible quebit
sea una mejor opciónfuente
null
,false
,true
- me pregunto si hay algo más limpio ...Servidor SQL:
Cómo hacerlo:
La mejor manera es un índice filtrado. Utiliza DRI
SQL Server 2008+
Columna calculada con singularidad. Utiliza DRI
Ver la respuesta de Jack Douglas. SQL Server 2005 y antes
Una vista indexada / materializada que es como un índice filtrado. Utiliza DRI
Todas las versiones.
Desencadenar. Utiliza código, no DRI.
Todas las versiones
Cómo no hacerlo:
Ver uno dos tres cuatro
fuente
PostgreSQL:
--editar
o (mucho mejor), use un índice parcial único :
fuente
Este tipo de problema es otra razón por la que solicité esta pregunta:
Configuración de la aplicación en la base de datos
Si tiene una tabla de configuración de aplicaciones en su base de datos, puede tener una entrada que haga referencia a la ID del registro que desea que se considere 'especial'. Luego, solo debe buscar cuál es la ID de su tabla de configuración, de esta manera no necesita una columna completa para solo un elemento que se está configurando.
fuente
Posibles enfoques utilizando tecnologías ampliamente implementadas:
1) Revocar los privilegios de 'escritor' sobre la mesa. Cree procedimientos CRUD que garanticen que la restricción se aplique en los límites de la transacción.
2) 6NF: suelte la
CHAR(1)
columna. Agregue una tabla de referencia restringida para garantizar que su cardinalidad no pueda exceder uno:Cambie la semántica de la aplicación para que el 'valor predeterminado' considerado sea la fila en la nueva tabla. Posiblemente use vistas para encapsular esta lógica.
3) Suelta la
CHAR(1)
columna. Agrega unaseq
columna entera. Poner una restricción únicaseq
. Cambie la semántica de la aplicación para que el 'valor predeterminado' considerado sea la fila donde elseq
valor es uno o elseq
valor más grande / más pequeño o similar. Posiblemente use vistas para encapsular esta lógica.fuente
Para aquellos que usan MySQL, aquí hay un procedimiento almacenado apropiado:
Para asegurarse de que su tabla esté limpia y que el procedimiento almacenado funcione, suponiendo que la ID 200 sea la predeterminada, ejecute estos pasos:
Aquí hay un disparador que también ayuda:
Para asegurarse de que su tabla esté limpia y que el disparador funcione, suponiendo que la ID 200 sea la predeterminada, ejecute estos pasos:
Darle una oportunidad !!!
fuente
En SQL Server 2000 y versiones posteriores, puede usar Vistas indexadas para implementar restricciones complejas (o de varias tablas) como la que está solicitando.
Además, Oracle tiene una implementación similar para vistas materializadas con restricciones de verificación diferida.
Mira mi publicación aquí .
fuente
SQL-92 de transición estándar, ampliamente implementado, por ejemplo, SQL Server 2000 y superior:
Revocar los privilegios de 'escritor' de la tabla. Cree dos vistas para
WHERE chk = 'Y'
yWHERE chk = 'N'
respectivamente, incluidoWITH CHECK OPTION
. Para laWHERE chk = 'Y'
vista, incluya una condición de búsqueda para que su cardinalidad no pueda exceder una. Otorgue privilegios de 'escritor' en las vistas.Código de ejemplo para las vistas:
fuente
Aquí hay una solución para MySQL y MariaDB usando columnas virtuales que es un poco más elegante. Requiere MySQL> = 5.7.6 o MariaDB> = 5.2:
Cree una columna virtual que sea NULL si no desea aplicar la restricción única:
(Para MySQL, use en
STORED
lugar dePERSISTENT
).fuente
SQL-92 COMPLETO estándar: use una subconsulta en una
CHECK
restricción, no ampliamente implementada, por ejemplo, compatible con Access2000 (ACE2007, Jet 4.0, lo que sea) y superior cuando está en modo de consulta ANSI-92 .Código de ejemplo: las
CHECK
restricciones de nota en Access siempre son a nivel de tabla. Debido a que laCREATE TABLE
declaración en la pregunta usa unaCHECK
restricción de nivel de fila , debe modificarse ligeramente agregando una coma:fuente
Solo hojeé las respuestas, así que podría haber perdido una respuesta similar. La idea es utilizar una columna generada que sea pk o una constante que no exista como valor para pk
AFAIK esto es válido en SQL2003 (ya que estaba buscando una solución agnóstica). DB2 lo permite, no estoy seguro de cuántos otros proveedores lo aceptan.
fuente