ORACLE no permite valores NULL en ninguna de las columnas que comprenden una clave primaria. Parece que lo mismo es cierto para la mayoría de los otros sistemas de "nivel empresarial".
Al mismo tiempo, la mayoría de los sistemas también permiten restricciones únicas en columnas anulables.
¿Por qué las restricciones únicas pueden tener valores NULL pero las claves primarias no? ¿Existe una razón lógica fundamental para esto o es más una limitación técnica?
database
database-design
Roman Starkov
fuente
fuente
Respuestas:
Las claves primarias son para identificar filas de manera única. Esto se hace comparando todas las partes de una clave con la entrada.
Por definición, NULL no puede ser parte de una comparación exitosa. Incluso una comparación con sí misma (
NULL = NULL
) fallará. Esto significa que una clave que contiene NULL no funcionaría.Además, NULL está permitido en una clave externa, para marcar una relación opcional. (*) Permitirlo en el PK también rompería esto.
(*) Una advertencia: tener claves externas anulables no es un diseño de base de datos relacional limpio.
Si hay dos entidades
A
yB
dóndeA
se pueden relacionar opcionalmenteB
, la solución limpia es crear una tabla de resolución (digamosAB
). Esa mesa enlazaríaA
conB
: Si no es una relación, entonces sería contener un registro, si existe , no es entonces no lo haría.fuente
Una clave primaria define un identificador único para cada fila de una tabla: cuando una tabla tiene una clave primaria, tiene una forma garantizada de seleccionar cualquier fila de la misma.
Una restricción única no necesariamente identifica cada fila; solo especifica que si una fila tiene valores en sus columnas, entonces deben ser únicos. Esto no es suficiente para identificar de manera única cada fila, que es lo que debe hacer una clave principal.
fuente
Básicamente, nada está mal con un NULL en una clave primaria de varias columnas. Pero tener una tiene implicaciones que el diseñador probablemente no tuvo la intención, por lo que muchos sistemas arrojan un error cuando lo intentas.
Considere el caso de las versiones de módulo / paquete almacenadas como una serie de campos:
Los primeros 5 elementos de la clave primaria son partes definidas regularmente de una versión de lanzamiento, pero algunos paquetes tienen una extensión personalizada que generalmente no es un número entero (como "rc-foo" o "vanilla" o "beta" o cualquier otra cosa para alguien con quien cuatro campos son insuficientes podría soñar). Si un paquete no tiene una extensión, entonces es NULL en el modelo anterior, y no se haría ningún daño al dejar las cosas de esa manera.
Pero, ¿qué es un NULL? Se supone que representa una falta de información, una incógnita. Dicho esto, quizás esto tenga más sentido:
En esta versión, la parte "ext" de la tupla NO ES NULA, pero por defecto es una cadena vacía, que es semánticamente (y prácticamente) diferente de una NULL. Un NULL es un desconocido, mientras que una cadena vacía es un registro deliberado de "algo que no está presente". En otras palabras, "vacío" y "nulo" son cosas diferentes. Es la diferencia entre "No tengo un valor aquí" y "No sé cuál es el valor aquí".
Cuando registra un paquete que carece de una extensión de versión, sabe que carece de una extensión, por lo que una cadena vacía es realmente el valor correcto. Un NULL solo sería correcto si no supiera si tiene una extensión o no, o si supiera que sí, pero no sabía qué era. Esta situación es más fácil de tratar en sistemas donde los valores de cadena son la norma, porque no hay forma de representar un "entero vacío" que no sea insertar 0 o 1, lo que terminará enrollando en las comparaciones realizadas más tarde (que tiene sus propias implicaciones) *.
Por cierto, ambas formas son válidas en Postgres (ya que estamos discutiendo los RDMBS "empresariales"), pero los resultados de la comparación pueden variar bastante cuando se agrega un NULL a la mezcla, porque NULL == "no sabe" así que todos Los resultados de una comparación que involucra un NULL terminan siendo NULL ya que no se puede saber algo que se desconoce. ¡PELIGRO! Piénselo bien: esto significa que los resultados de comparación NULL se propagan a través de una serie de comparaciones. Esto puede ser una fuente de errores sutiles al ordenar, comparar, etc.
Postgres asume que eres un adulto y puede tomar esta decisión por ti mismo. Oracle y DB2 asumen que no se dio cuenta de que estaba haciendo algo tonto y arrojan un error. Por lo general, esto es lo correcto, pero no siempre; en realidad , es posible que no sepa y tenga un NULL en algunos casos y, por lo tanto, dejar una fila con un elemento desconocido contra el cual las comparaciones significativas son imposibles es un comportamiento correcto.
En cualquier caso, debe esforzarse por eliminar el número de campos NULL que permite en todo el esquema y de manera doble cuando se trata de campos que son parte de una clave primaria. En la gran mayoría de los casos, la presencia de columnas NULL es una indicación de un diseño de esquema no normalizado (en oposición a un desalineamiento deliberado) y debe pensarse mucho antes de ser aceptado.
[* NOTA: es posible crear un tipo personalizado que sea la unión de enteros y un tipo "inferior" que significaría semánticamente "vacío" en lugar de "desconocido". Desafortunadamente, esto introduce un poco de complejidad en las operaciones de comparación y, por lo general, ser verdaderamente correcto en escribir no vale la pena el esfuerzo en la práctica, ya que no se le deberían permitir muchos
NULL
valores en primer lugar. Dicho esto, sería maravilloso si los RDBMS incluyeran unBOTTOM
tipo predeterminado ademásNULL
de evitar el hábito de combinar casualmente la semántica de "sin valor" con "valor desconocido". ]fuente
NULL == NULL -> false (al menos en DBMS)
Por lo tanto, no podrá recuperar ninguna relación utilizando un valor NULL incluso con columnas adicionales con valores reales.
fuente
where pk_1 = 'a' and pk_2 = 'b'
valores normales y cambiar awhere pk_1 is null and pk_2 = 'b'
cuando hay valores nulos.where (a.pk1 = b.pk1 or (a.pk1 is null and b.pk1 is null)) and (a.pk2 = b.pk2 or (a.pk2 is null and b.pk2 is null))
/La respuesta de Tony Andrews es decente. Pero la respuesta real es que esta ha sido una convención utilizada por la comunidad de bases de datos relacionales y NO es una necesidad. Tal vez sea una buena convención, tal vez no.
Comparar cualquier cosa con NULL resulta en DESCONOCIDO (3er valor de verdad). Entonces, como se ha sugerido con nulos, toda la sabiduría tradicional sobre la igualdad se va por la ventana. Bueno, así es como parece a primera vista.
Pero no creo que esto sea necesariamente así, e incluso las bases de datos SQL no creen que NULL destruya toda posibilidad de comparación.
Ejecute en su base de datos la consulta SELECT * FROM VALUES (NULL) UNION SELECT * FROM VALUES (NULL)
Lo que ves es solo una tupla con un atributo que tiene el valor NULL. Entonces la unión reconoció aquí los dos valores NULL como iguales.
Al comparar una clave compuesta que tiene 3 componentes con una tupla con 3 atributos (1, 3, NULL) = (1, 3, NULL) <=> 1 = 1 AND 3 = 3 AND NULL = NULL El resultado de esto es DESCONOCIDO .
Pero podríamos definir un nuevo tipo de operador de comparación, por ejemplo. ==. X == Y <=> X = Y OR (X ES NULO E Y ES NULO)
Tener este tipo de operador de igualdad haría que las claves compuestas con componentes nulos o las claves no compuestas con valor nulo no sean problemáticas.
fuente
Todavía creo que este es un defecto fundamental / funcional provocado por un tecnicismo. Si tiene un campo opcional por el cual puede identificar a un cliente, ahora tiene que hackear un valor ficticio, solo porque NULL! = NULL, no es particularmente elegante pero es un "estándar de la industria"
fuente