Técnicamente, NULL = NULL es falso, por esa lógica no NULL es igual a ningún NULL y todos los NULL son distintos. ¿No debería implicar que todos los NULL son únicos y que un índice único debería permitir cualquier número de NULL?
Técnicamente, NULL = NULL es falso, por esa lógica no NULL es igual a ningún NULL y todos los NULL son distintos. ¿No debería implicar que todos los NULL son únicos y que un índice único debería permitir cualquier número de NULL?
Respuestas:
¿Por qué funciona de esta manera? Porque hace mucho tiempo, alguien tomó una decisión de diseño sin saber o preocuparse por lo que dice el estándar (después de todo, tenemos todo tipo de comportamientos extraños con NULL
s, y podemos forzar diferentes comportamientos a voluntad). Esa decisión dicta que, en este caso NULL = NULL
,.
No fue una decisión muy inteligente. Lo que deberían haber hecho es que el comportamiento predeterminado se adhiera al estándar ANSI, y si realmente querían este comportamiento peculiar, permitirlo a través de una opción DDL como WITH CONSIDER_NULLS_EQUAL
o WITH ALLOW_ONLY_ONE_NULL
.
Por supuesto, la retrospectiva es 20/20.
Y tenemos una solución, ahora, de todos modos, incluso si no es la más limpia o más intuitiva.
Puede obtener el comportamiento ANSI adecuado en SQL Server 2008 y superior creando un índice único y filtrado.
CREATE UNIQUE INDEX foo ON dbo.bar(key) WHERE key IS NOT NULL;
Esto permite más de un NULL
valor porque esas filas quedan completamente fuera de la verificación duplicada. Como una ventaja adicional, esto terminaría siendo un índice más pequeño que uno que consistía en la tabla completa si NULL
se permitieran múltiples s (especialmente cuando no es la única columna en el índice, tiene INCLUDE
columnas, etc.). Sin embargo, es posible que desee conocer algunas de las otras limitaciones de los índices filtrados:
Correcto. La implementación de una restricción o índice único en el servidor sql permite uno y solo un NULL. También corrija que esto técnicamente no encaja con la definición de NULL, pero es una de esas cosas que hicieron para hacerlo más útil a pesar de que no es "técnicamente" correcto. Tenga en cuenta que una CLAVE PRIMARIA (también un índice único) no permite NULL (por supuesto).
Primero, deja de usar la frase "Valor nulo", solo te llevará por mal camino. En su lugar, use la frase "marcador nulo": un marcador en una columna que indica que el valor real en esta columna falta o no es aplicable (pero tenga en cuenta que el marcador no dice cuál de esas opciones es realmente el caso¹).
Ahora, imagine lo siguiente (donde la base de datos no tiene un conocimiento completo de la situación modelada).
Situation Database
ID Code ID Code
-- ----- -- -----
1 A 1 A
2 B 2 (null)
3 C 3 C
4 B 4 (null)
La regla de integridad que estamos modelando es "el Código debe ser único". La situación del mundo real viola esto, por lo que la base de datos no debe permitir que los elementos 2 y 4 estén en la tabla al mismo tiempo.
El enfoque más seguro y menos flexible sería no permitir marcadores nulos en el campo Código, por lo que no hay posibilidad de datos inconsistentes. El enfoque más flexible sería permitir múltiples marcadores nulos y preocuparse por la unicidad cuando se ingresan los valores.
Los programadores de Sybase adoptaron el enfoque algo seguro y no muy flexible de permitir solo un marcador nulo en la tabla, algo de lo que los comentaristas se han quejado desde entonces. Microsoft ha continuado este comportamiento, supongo que para la compatibilidad con versiones anteriores.
¹ Estoy seguro de que leí en alguna parte que Codd consideró implementar dos marcadores nulos, uno para desconocido, otro para inaplicable, pero lo rechazó, pero no puedo encontrar la referencia. ¿Estoy recordando correctamente?
PD: Mi cita favorita sobre nulo: Louis Davidson, "Diseño profesional de la base de datos de SQL Server 2000", Wrox Press, 2001, página 52. "En resumen, una sola oración: NULL es malo".
null
tampoco logra este objetivo. Porque el valor faltante podría ser el mismo que el valor en una de las otras filas.
CHECK (Value IN ('A','B','C','D'))
? Luego, tanto la implementación de SQL-Server como el estándar SQL permiten que la tabla tenga 5 filas (una fila para cada valor más 1 con NULL). Luego, posiblemente, mientras la base de datos es consistente con sus restricciones, no es consistente con la intención del diseñador para la tabla tiene un máximo de 4 filas. No hay ningún valor en el que se pueda cambiar el valor NULL que no viole una restricción, a menos que se eliminen una o más filas.
CREATE TABLE #T(A INT NULL UNIQUE);INSERT INTO #T VALUES (1),(NULL);UPDATE #T SET A = 1 WHERE A IS NULL;
provocará un error. Según su teoría de las motivaciones de diseño, debería haber evitado la inserción NULL
en el primer caso, porque el conocimiento incompleto significa que no hay garantía de que el valor sea diferente.
Esto puede no ser técnicamente exacto, pero filosóficamente me ayuda a dormir por la noche ...
Como varios otros han dicho o aludido, si piensa en NULL como desconocido, entonces no puede determinar si un valor NULL es de hecho igual a otro valor NULL. Pensando en esto de esta manera, la expresión NULL == NULL debería evaluar a NULL, lo que significa desconocido.
Una restricción Unique necesitaría un valor definitivo para la comparación de los valores de la columna. En otras palabras, cuando se compara un valor de una sola columna con cualquier otro valor de la columna utilizando el operador de igualdad, debe evaluar como falso para ser válido. Desconocido no es realmente falso a pesar de que a menudo se trata como falso. Dos valores NULL pueden ser iguales, o no ... simplemente no se puede determinar definitivamente.
Ayuda a pensar en una restricción única como valores restrictivos que se pueden determinar como distintos entre sí. Lo que quiero decir con esto es si ejecuta un SELECT que se parece a esto:
SELECT * from dbo.table1 WHERE ColumnWithUniqueContraint="some value"
La mayoría de las personas esperaría un resultado, dado que existe una restricción única. Si permitiste múltiples valores NULL en ColumnWithUniqueConstraint, entonces sería imposible seleccionar una sola fila distinta de la tabla usando NULL como valor comparado.
Dado eso, creo que independientemente de si se implementa con precisión o no con respecto a la definición de NULL, definitivamente es mucho más práctico en la mayoría de las situaciones que permitir múltiples valores NULL.
Uno de los propósitos principales de una UNIQUE
restricción es evitar registros duplicados. Si uno necesita tener una tabla en la que puede haber múltiples registros donde un valor es "desconocido", pero no se permite que dos registros tengan el mismo valor "conocido", entonces a los valores desconocidos se les deben asignar identificadores únicos artificiales antes de que sean añadido a la mesa.
Hay algunos casos raros en los que una columna que tiene una UNIQUE
restricción y contiene un solo valor nulo; por ejemplo, si una tabla contiene un mapeo entre valores de columna y descripciones de texto localizadas, una fila para NULL
permitiría definir la descripción que debería aparecer cuando esa columna en alguna otra tabla lo es NULL
. El comportamiento de NULL
permite ese caso de uso.
De lo contrario, no veo ninguna base para una base de datos con una UNIQUE
restricción en ninguna columna para permitir la existencia de muchos registros idénticos, pero no veo ninguna forma de evitar eso mientras se permiten múltiples registros cuyos valores clave no son distinguibles. Declarar que NULL
no es igual a sí mismo no hará que los NULL
valores sean distinguibles entre sí.