Quiero exigir que solo un registro en una tabla se considere el valor "predeterminado" para otras consultas o vistas que puedan acceder a esa tabla.
Básicamente, quiero garantizar que esta consulta siempre devolverá exactamente una fila:
SELECT ID, Zip
FROM PostalCodes
WHERE isDefault=True
¿Cómo haría eso en SQL?
mysql
database-design
emaynard
fuente
fuente
PostalCodes
está vacío? Si una fila ya tiene una propiedad que debería tener, ¿se evitará que se establezca en falso a menos que otra fila (si existe) se establezca en verdadero dentro de la misma instrucción SQL? ¿Pueden las filas cero tener la propiedad entre los límites de la transacción? ¿Debería forzarse la última fila de la tabla a tener la propiedad y evitar que se elimine? La experiencia me dice que "garantizar exactamente una fila" tiende a significar algo diferente en la realidad, a menudo simplemente "como máximo una fila".Respuestas:
Editar: esto está orientado a SQL Server antes de que supiéramos MySQL
Hay
45 formas de hacer esto: en orden de más deseado a menos deseadoSin embargo, no sabemos para qué es RDBMS, por lo que no todos pueden aplicarse
La mejor manera es un índice filtrado. Esto usa DRI para mantener la unicidad.
Columna calculada con unicidad (ver la respuesta de Jack Douglas) (agregado por edit 2)
Una vista indexada / materializada que es como un índice filtrado usando DRI
Disparador (según otras respuestas)
Verifique la restricción con un UDF. Esto no es seguro para la concurrencia y el aislamiento de instantáneas. Ver uno dos tres cuatro
Tenga en cuenta que el requisito de "un valor predeterminado" está en la tabla, no en el procedimiento almacenado. El procedimiento almacenado tendrá los mismos problemas de concurrencia que una restricción de verificación con un udf
Nota: preguntado en tantas veces:
fuente
Nota: Esta respuesta se dio antes de que quedara claro que el autor de la pregunta quería algo específico de MySQL. Esta respuesta está sesgada hacia SQL Server.
Aplique una restricción CHECK a la tabla que llama a un UDF y se asegura de que su valor de retorno sea <= 1. El UDF solo puede contar las filas de la tabla DONDE
isDefault = TRUE
. Esto asegurará que no haya más de 1 fila predeterminada en la tabla.Debe agregar un mapa de bits o un índice filtrado en la
isDefault
columna (según su plataforma) para asegurarse de que este UDF se ejecute muy rápidamente.Advertencias
PostalCodes
tabla, sin embargo, esto sigue siendo una preocupación de rendimiento para aquellas situaciones en las que es probable una actividad basada en conjuntos.Dadas todas estas advertencias, recomiendo usar la sugerencia de gbn de un índice único filtrado en lugar de una restricción de verificación.
fuente
Aquí hay un procedimiento almacenado (dialecto MySQL):
Para asegurarse de que su tabla esté limpia y que el procedimiento almacenado esté funcionando, suponiendo que la ID 200 sea la predeterminada, ejecute estos pasos:
En lugar de un procedimiento almacenado, ¿qué tal un disparador?
Para asegurarse de que su tabla esté limpia y que el disparador funcione, suponiendo que la ID 200 sea la predeterminada, ejecute estos pasos:
fuente
Podría usar un disparador para aplicar las reglas. Cuando una instrucción UPDATE o INSERT establece isDefault en True, el SQL en el desencadenador puede establecer todas las demás filas en False.
Tendrás que considerar otras situaciones al aplicar esto. Por ejemplo, ¿qué debería suceder si más de una fila y los conjuntos ACTUALIZAR o INSERTAR son predeterminados a verdaderos? ¿Qué reglas se aplicarán para evitar romper la regla?
Además, ¿es posible la condición donde no hay un defecto? Qué sucederá cuando una actualización establezca isDefault de True a False.
Una vez que defina las reglas, puede construirlas en el activador.
Luego, como se indica en otra respuesta, desea aplicar una restricción de verificación para ayudar a hacer cumplir las reglas.
fuente
A continuación se configura una tabla y datos de ejemplo:
Si crea un procedimiento almacenado para establecer el indicador IsDefault y eliminar los permisos para cambiar la tabla base, puede aplicar esto con la consulta a continuación. Se requeriría un escaneo de tabla cada vez.
Dependiendo del tamaño de la tabla, un índice en IsDefault (DESC) puede generar una o ambas consultas a continuación, evitando un análisis completo.
Si no puede eliminar los permisos de la tabla base, necesita hacer cumplir la integridad por otros medios y está utilizando SQL2008, puede utilizar un índice único filtrado:
fuente
Para MySQL que no tiene índices filtrados, puede hacer algo como lo siguiente. Explota el hecho de que MySQL permite múltiples NULL en índices únicos y utiliza una tabla dedicada y una clave foránea para simular un tipo de datos que solo puede tener NULL y 1 como valores.
Con esta solución, en lugar de verdadero / falso, solo usaría 1 / NULL.
Creo que lo único que puede salir mal es si alguien agrega una fila a la
DefaultFlag
tabla. Creo que esto no es una gran preocupación, y siempre puedes arreglarlo con permisos si realmente quieres estar seguro.fuente
Básicamente, esto te estaba dando problemas porque colocaste el campo en el lugar equivocado y eso es todo.
Tenga una tabla 'Defaults', con PostalCode, que contenga la identificación que está buscando. En general, también es bueno nombrar sus tablas de acuerdo con un registro, por lo que el nombre de su tabla inicial sería mejor llamado 'Código postal'. Los valores predeterminados serán una tabla de una sola fila, hasta que necesite especializar sus valores predeterminados en alguna otra configuración (como el historial).
La consulta final que desea es la siguiente:
fuente