En SQL , null o NULL es un marcador especial que se utiliza para indicar que un valor de datos no existe en la base de datos . Introducido por el creador del modelo de base de datos relacional , E. F. Codd , el valor null de SQL sirve para cumplir con el requisito de que todos los sistemas de gestión de bases de datos relacionales ( RDBMS ) verdaderos admitan una representación de "información faltante e información inaplicable". Codd también introdujo el uso del símbolo griego omega (ω) en minúscula para representar null en la teoría de bases de datos . En SQL, NULL
es una palabra reservada que se utiliza para identificar este marcador.
Un valor nulo no debe confundirse con un valor de 0. Un valor nulo indica la falta de un valor, que no es lo mismo que un valor cero. Por ejemplo, considere la pregunta "¿Cuántos libros tiene Adam?" La respuesta puede ser "cero" (sabemos que no tiene ninguno ) o "nulo" ( no sabemos cuántos tiene). En una tabla de base de datos, la columna que informa esta respuesta comenzaría sin ningún valor (marcada como nula) y no se actualizaría con el valor cero hasta que se determine que Adam no tiene ningún libro.
En SQL, null es un marcador, no un valor. Este uso es bastante diferente al de la mayoría de los lenguajes de programación, donde un valor nulo de una referencia significa que no apunta a ningún objeto .
EF Codd mencionó los valores nulos como un método para representar datos faltantes en el modelo relacional en un artículo de 1975 en el Boletín FDT de ACM - SIGMOD . El artículo de Codd que se cita con más frecuencia con la semántica de los valores nulos (tal como se adoptan en SQL) es su artículo de 1979 en ACM Transactions on Database Systems , en el que también presentó su Relational Model/Tasmania , aunque muchas de las otras propuestas de este último artículo han permanecido oscuras. La sección 2.3 de su artículo de 1979 detalla la semántica de la propagación de valores nulos en operaciones aritméticas, así como las comparaciones que emplean una lógica ternaria (de tres valores) al comparar con valores nulos; también detalla el tratamiento de los valores nulos en otras operaciones de conjuntos (este último tema aún es controvertido en la actualidad). En los círculos de la teoría de bases de datos , la propuesta original de Codd (1975, 1979) ahora se conoce como "tablas de Codd". [1] Posteriormente, Codd reforzó su requisito de que todos los RDBMS admitieran Null para indicar datos faltantes en un artículo de dos partes de 1985 publicado en la revista Computerworld . [2] [3]
El estándar SQL de 1986 básicamente adoptó la propuesta de Codd después de un prototipo de implementación en IBM System R. Aunque Don Chamberlin reconoció los valores nulos (junto con las filas duplicadas) como una de las características más controvertidas de SQL, defendió el diseño de los valores nulos en SQL invocando los argumentos pragmáticos de que era la forma menos costosa de soporte del sistema para la información faltante, ahorrando al programador muchas comprobaciones duplicadas a nivel de aplicación (ver problema del semipredicado ) mientras que al mismo tiempo proporciona al diseñador de la base de datos la opción de no utilizar valores nulos si así lo desea; por ejemplo, para evitar anomalías bien conocidas (discutidas en la sección de semántica de este artículo). Chamberlin también argumentó que además de proporcionar alguna funcionalidad de valor faltante, la experiencia práctica con los valores nulos también llevó a otras características del lenguaje que dependen de los valores nulos, como ciertas construcciones de agrupación y uniones externas. Finalmente, argumentó que en la práctica los valores nulos también terminan siendo utilizados como una forma rápida de parchar un esquema existente cuando necesita evolucionar más allá de su intención original, codificando no información faltante sino información inaplicable; por ejemplo, una base de datos que necesita rápidamente soportar autos eléctricos mientras tiene una columna de millas por galón. [4]
Codd indicó en su libro de 1990 The Relational Model for Database Management, Version 2 que el único valor nulo exigido por el estándar SQL era inadecuado y debería ser reemplazado por dos marcadores de tipo nulo separados para indicar por qué faltan datos. En el libro de Codd, estos dos marcadores de tipo nulo se denominan "valores A" y "valores I", que representan "faltante pero aplicable" y "faltante pero inaplicable", respectivamente. [5] La recomendación de Codd habría requerido que el sistema lógico de SQL se expandiera para dar cabida a un sistema lógico de cuatro valores. Debido a esta complejidad adicional, la idea de múltiples valores nulos con diferentes definiciones no ha ganado una aceptación generalizada en el dominio de los profesionales de las bases de datos. Sin embargo, sigue siendo un campo de investigación activo, con numerosos artículos aún en publicación.
Null ha sido el foco de controversia y una fuente de debate debido a su lógica de tres valores asociada (3VL), requisitos especiales para su uso en uniones SQL y el manejo especial requerido por funciones agregadas y operadores de agrupación SQL. El profesor de informática Ron van der Meyden resumió los diversos problemas como: "Las inconsistencias en el estándar SQL significan que no es posible atribuir ninguna semántica lógica intuitiva al tratamiento de los valores nulos en SQL". [1] Aunque se han hecho varias propuestas para resolver estos problemas, la complejidad de las alternativas ha impedido su adopción generalizada.
Como Null no es un valor de datos, sino un marcador de un valor ausente, el uso de operadores matemáticos en Null da un resultado desconocido, que se representa por Null. [6] En el siguiente ejemplo, multiplicar 10 por Null da como resultado Null:
10 * NULL -- El resultado es NULL
Esto puede generar resultados imprevistos. Por ejemplo, cuando se intenta dividir Null por cero, las plataformas pueden devolver Null en lugar de lanzar una "excepción de datos esperada: división por cero". [6] Aunque este comportamiento no está definido por el estándar SQL ISO, muchos proveedores de DBMS tratan esta operación de manera similar. Por ejemplo, las plataformas Oracle, PostgreSQL, MySQL Server y Microsoft SQL Server devuelven un resultado Null para lo siguiente:
NULO / 0
Las operaciones de concatenación de cadenas , que son comunes en SQL, también dan como resultado Null cuando uno de los operandos es Null. [7] El siguiente ejemplo demuestra el resultado Null devuelto al usar Null con el ||
operador de concatenación de cadenas SQL.
'Pescado' || NULL || 'Papas fritas' -- El resultado es NULL
Esto no es así en todas las implementaciones de bases de datos. En un RDBMS de Oracle, por ejemplo, NULL y la cadena vacía se consideran lo mismo y, por lo tanto, 'Fish ' || NULL || 'Chips' da como resultado 'Fish Chips'. [8]
Dado que Null no es miembro de ningún dominio de datos , no se considera un "valor", sino más bien un marcador (o marcador de posición) que indica el valor indefinido . Debido a esto, las comparaciones con Null nunca pueden dar como resultado Verdadero o Falso, sino siempre un tercer resultado lógico, Desconocido. [9] El resultado lógico de la siguiente expresión, que compara el valor 10 con Null, es Desconocido:
SELECT 10 = NULL -- Resultados desconocidos
Sin embargo, ciertas operaciones sobre valores nulos pueden devolver valores si el valor ausente no es relevante para el resultado de la operación. Considere el siguiente ejemplo:
SELECCIONAR NULO O VERDADERO – Da como resultado Verdadero
En este caso, el hecho de que el valor a la izquierda de OR sea desconocido es irrelevante, porque el resultado de la operación OR sería Verdadero independientemente del valor a la izquierda.
SQL implementa tres resultados lógicos, por lo que las implementaciones de SQL deben proporcionar una lógica de tres valores especializada (3VL) . Las reglas que rigen la lógica de tres valores de SQL se muestran en las tablas siguientes ( p y q representan estados lógicos)" [10] Las tablas de verdad que SQL utiliza para AND, OR y NOT corresponden a un fragmento común de la lógica de tres valores de Kleene y Łukasiewicz (que difieren en su definición de implicación, sin embargo, SQL no define dicha operación). [11]
pag | q | p o q | p y q | p = q |
---|---|---|---|---|
Verdadero | Verdadero | Verdadero | Verdadero | Verdadero |
Verdadero | FALSO | Verdadero | FALSO | FALSO |
Verdadero | Desconocido | Verdadero | Desconocido | Desconocido |
FALSO | Verdadero | Verdadero | FALSO | FALSO |
FALSO | FALSO | FALSO | FALSO | Verdadero |
FALSO | Desconocido | Desconocido | FALSO | Desconocido |
Desconocido | Verdadero | Verdadero | Desconocido | Desconocido |
Desconocido | FALSO | Desconocido | FALSO | Desconocido |
Desconocido | Desconocido | Desconocido | Desconocido | Desconocido |
pag | NO p |
---|---|
Verdadero | FALSO |
FALSO | Verdadero |
Desconocido | Desconocido |
La lógica de tres valores de SQL se encuentra en el lenguaje de manipulación de datos (DML) en predicados de comparación de sentencias y consultas DML. La cláusula hace que la sentencia DML actúe solo en aquellas filas para las que el predicado se evalúa como Verdadero. Las sentencias , o DML WHERE
no actúan sobre las filas para las que el predicado se evalúa como Falso o Desconocido , y las consultas las descartan . Interpretar Desconocido y Falso como el mismo resultado lógico es un error común que se encuentra al tratar con valores nulos. [10] El siguiente ejemplo simple demuestra esta falacia:INSERT
UPDATE
DELETE
SELECT
SELECCIONAR * DE t DONDE i = NULO ;
La consulta de ejemplo anterior siempre devuelve cero filas de manera lógica porque la comparación de la columna i con Null siempre devuelve Unknown, incluso para aquellas filas donde i es Null. El resultado Unknown hace que la SELECT
instrucción descarte sumariamente todas las filas. (Sin embargo, en la práctica, algunas herramientas SQL recuperarán filas mediante una comparación con Null).
Los operadores de comparación SQL básicos siempre devuelven Unknown al comparar cualquier cosa con Null, por lo que el estándar SQL proporciona dos predicados de comparación especiales específicos de Null. Los predicados IS NULL
and IS NOT NULL
(que utilizan una sintaxis de sufijo ) prueban si los datos son o no Null. [12]
El estándar SQL contiene la característica opcional F571 "Pruebas de valor de verdad" que introduce tres operadores unarios lógicos adicionales (seis de hecho, si contamos su negación, que forma parte de su sintaxis), que también utilizan notación sufija. Tienen las siguientes tablas de verdad: [13]
pag | p ES VERDADERO | p NO ES VERDADERO | p ES FALSO | p NO ES FALSO | p ES DESCONOCIDO | p NO ES DESCONOCIDO |
---|---|---|---|---|---|---|
Verdadero | Verdadero | FALSO | FALSO | Verdadero | FALSO | Verdadero |
FALSO | FALSO | Verdadero | Verdadero | FALSO | FALSO | Verdadero |
Desconocido | FALSO | Verdadero | FALSO | Verdadero | Verdadero | FALSO |
La característica F571 es ortogonal a la presencia del tipo de datos Boolean en SQL (discutido más adelante en este artículo) y, a pesar de las similitudes sintácticas, F571 no introduce literales Boolean o de tres valores en el lenguaje. La característica F571 estaba presente en SQL92 , [14] mucho antes de que el tipo de datos Boolean fuera introducido en el estándar en 1999. Sin embargo, la característica F571 es implementada por pocos sistemas; PostgreSQL es uno de los que la implementan.
La adición de IS UNKNOWN a los demás operadores de la lógica de tres valores de SQL hace que la lógica de tres valores de SQL sea funcionalmente completa , [15] lo que significa que sus operadores lógicos pueden expresar (en combinación) cualquier función lógica de tres valores concebible.
En sistemas que no admiten la función F571, es posible emular IS UNKNOWN p revisando cada argumento que podría hacer que la expresión p sea desconocida y probando esos argumentos con IS NULL u otras funciones específicas de NULL, aunque esto puede ser más engorroso.
En la lógica de tres valores de SQL, la ley del medio excluido , p OR NOT p , ya no se evalúa como verdadera para todos los valores p . Más precisamente, en la lógica de tres valores de SQL , p OR NOT p es desconocido precisamente cuando p es desconocido y verdadero en caso contrario. Debido a que las comparaciones directas con Null dan como resultado el valor lógico desconocido, la siguiente consulta
SELECCIONAR * DE cosas DONDE ( x = 10 ) O NO ( x = 10 );
no es equivalente en SQL con
SELECCIONAR * DE cosas ;
si la columna x contiene algún valor nulo; en ese caso, la segunda consulta devolvería algunas filas que la primera no devuelve, es decir, todas aquellas en las que x es nulo. En la lógica clásica de dos valores, la ley del medio excluido permitiría la simplificación del predicado de la cláusula WHERE, de hecho su eliminación. Intentar aplicar la ley del medio excluido a la 3VL de SQL es efectivamente una falsa dicotomía . La segunda consulta es en realidad equivalente a:
SELECT * FROM stuff ; -- es (debido a 3VL) equivalente a: SELECT * FROM stuff WHERE ( x = 10 ) OR NOT ( x = 10 ) OR x IS NULL ;
Por lo tanto, para simplificar correctamente la primera declaración en SQL se requiere que devolvamos todas las filas en las que x no sea nulo.
SELECCIONAR * DE cosas DONDE x NO ES NULO ;
En vista de lo anterior, observe que para la cláusula WHERE de SQL se puede escribir una tautología similar a la ley del tercero excluido. Suponiendo que el operador IS UNKNOWN está presente, p OR (NOT p ) OR ( p IS UNKNOWN) es verdadero para cada predicado p . Entre los lógicos, esto se llama ley del cuarto excluido .
Hay algunas expresiones SQL en las que es menos obvio dónde ocurre el falso dilema, por ejemplo:
SELECCIONAR 'ok' DONDE 1 NO EN ( SELECCIONAR CAST ( NULO COMO ENTERO )) UNIÓN SELECCIONAR 'ok' DONDE 1 EN ( SELECCIONAR CAST ( NULO COMO ENTERO ));
no produce filas porque IN
se traduce a una versión iterada de igualdad sobre el conjunto de argumentos y 1<>NULL es desconocido, al igual que 1=NULL es desconocido. (El CAST en este ejemplo es necesario solo en algunas implementaciones de SQL como PostgreSQL, que lo rechazarían con un error de verificación de tipo de lo contrario. En muchos sistemas, el SELECT NULL simple funciona en la subconsulta). El caso faltante anterior es, por supuesto:
SELECCIONAR 'ok' DONDE ( 1 EN ( SELECCIONAR CAST ( NULL COMO ENTERO ))) ES DESCONOCIDO ;
Las uniones se evalúan utilizando las mismas reglas de comparación que para las cláusulas WHERE. Por lo tanto, se debe tener cuidado al utilizar columnas que aceptan valores nulos en los criterios de unión de SQL. En particular, una tabla que contiene valores nulos no es igual a una autounión natural de sí misma, lo que significa que, mientras que es cierto para cualquier relación R en el álgebra relacional , una autounión de SQL excluirá todas las filas que tengan un valor nulo en cualquier lugar. [16] Se ofrece un ejemplo de este comportamiento en la sección que analiza la semántica de valores faltantes de los valores nulos.
COALESCE
La función o las expresiones SQL CASE
se pueden utilizar para "simular" la igualdad de valores nulos en los criterios de unión, y los predicados IS NULL
y IS NOT NULL
también se pueden utilizar en los criterios de unión. El siguiente predicado prueba la igualdad de los valores A y B y trata los valores nulos como si fueran iguales.
( A = B ) O ( A ES NULO Y B ES NULO )
SQL ofrece dos tipos de expresiones condicionales . Una se denomina "CASE simple" y funciona como una sentencia switch . La otra se denomina "CASE buscado" en el estándar y funciona como un if...elseif .
Las CASE
expresiones simples utilizan comparaciones de igualdad implícitas que funcionan bajo las mismas reglas que las WHERE
reglas de la cláusula DML para valores nulos. Por lo tanto, una expresión simpleCASE
no puede verificar la existencia de valores nulos directamente. Una verificación de valores nulos en una CASE
expresión simple siempre da como resultado Desconocido, como en el siguiente ejemplo:
SELECT CASE i WHEN NULL THEN 'Is Null' - Esto nunca se devolverá WHEN 0 THEN 'Is Zero' - Esto se devolverá cuando i = 0 WHEN 1 THEN 'Is One' - Esto se devolverá cuando i = 1 END FROM t ;
Debido a que la expresión i = NULL
se evalúa como Desconocido sin importar qué valor contenga la columna i (incluso si contiene Nulo), la cadena 'Is Null'
nunca se devolverá.
Por otra parte, una CASE
expresión "buscada" puede utilizar predicados como IS NULL
y IS NOT NULL
en sus condiciones. El siguiente ejemplo muestra cómo utilizar una CASE
expresión buscada para comprobar correctamente si hay valores nulos:
SELECCIONAR CASO CUANDO i ES NULO ENTONCES 'Resultado nulo' : esto se devolverá cuando i sea NULO CUANDO i = 0 ENTONCES 'Cero' : esto se devolverá cuando i = 0 CUANDO i = 1 ENTONCES 'Uno' : esto se devolverá cuando i = 1 FIN DE t ;
En la CASE
expresión buscada, la cadena 'Null Result'
se devuelve para todas las filas en las que i es nulo.
El dialecto de SQL de Oracle proporciona una función incorporada DECODE
que se puede utilizar en lugar de las expresiones CASE simples y considera que dos valores nulos son iguales.
SELECCIONAR DECODIFICAR ( i , NULL , ' Resultado nulo' , 0 , 'Cero' , 1 , 'Uno' ) DE t ;
Finalmente, todas estas construcciones devuelven un NULL si no se encuentra ninguna coincidencia; tienen una ELSE NULL
cláusula predeterminada.
SQL/PSM (SQL Persistent Stored Modules) define extensiones procedimentales para SQL, como la IF
sentencia. Sin embargo, los principales proveedores de SQL han incluido históricamente sus propias extensiones procedimentales patentadas. Las extensiones procedimentales para bucles y comparaciones funcionan bajo reglas de comparación Null similares a las de las sentencias y consultas DML. El siguiente fragmento de código, en formato estándar ISO SQL, demuestra el uso de Null 3VL en una IF
sentencia.
SI i = NULO ENTONCES SELECCIONA 'El resultado es Verdadero' DE LO CONTRARIO SI NO ( i = NULO ) ENTONCES SELECCIONA 'El resultado es Falso' DE LO CONTRARIO SELECCIONA 'El resultado es Desconocido' ;
La IF
instrucción realiza acciones solo para aquellas comparaciones que evalúan como Verdadero. Para las instrucciones que evalúan como Falso o Desconocido, la IF
instrucción pasa el control a la ELSEIF
cláusula y, finalmente, a la ELSE
cláusula. El resultado del código anterior siempre será el mensaje, 'Result is Unknown'
ya que las comparaciones con Nulo siempre evalúan como Desconocido.
El trabajo pionero de T. Imieliński y W. Lipski Jr. (1984) [17] proporcionó un marco en el que evaluar la semántica prevista de varias propuestas para implementar la semántica de valores faltantes, que se conoce como Álgebras de Imieliński-Lipski . Esta sección sigue aproximadamente el capítulo 19 del libro de texto "Alice". [18] Una presentación similar aparece en la revisión de Ron van der Meyden, §10.4. [1]
Las construcciones que representan información faltante, como las tablas Codd, en realidad tienen como objetivo representar un conjunto de relaciones, una para cada posible instanciación de sus parámetros; en el caso de las tablas Codd, esto significa reemplazar los valores nulos con algún valor concreto. Por ejemplo,
Nombre | Edad |
---|---|
Jorge | 43 |
Harriet | NULL |
Carlos | 56 |
Nombre | Edad |
---|---|
Jorge | 43 |
Harriet | 22 |
Carlos | 56 |
Nombre | Edad |
---|---|
Jorge | 43 |
Harriet | 37 |
Carlos | 56 |
Se dice que un constructo (como una tabla Codd) es un sistema de representación fuerte (de información faltante) si cualquier respuesta a una consulta realizada sobre el constructo puede particularizarse para obtener una respuesta para cualquier consulta correspondiente sobre las relaciones que representa, que se consideran modelos del constructo. Más precisamente, si q es una fórmula de consulta en el álgebra relacional (de relaciones "puras") y si q es su elevación a un constructo destinado a representar información faltante, una representación fuerte tiene la propiedad de que para cualquier consulta q y constructo (tabla) T , q eleva todas las respuestas al constructo, es decir:
(Lo anterior debe ser válido para consultas que toman cualquier número de tablas como argumentos, pero la restricción a una tabla es suficiente para esta discusión). Claramente, las tablas Codd no tienen esta propiedad fuerte si las selecciones y proyecciones se consideran parte del lenguaje de consulta. Por ejemplo, todas las respuestas a
SELECCIONAR * DE Emp DONDE Edad = 22 ;
Debería incluir la posibilidad de que exista una relación como EmpH22. Sin embargo, las tablas de Codd no pueden representar la disyunción "resultado con posiblemente 0 o 1 filas". Sin embargo, un dispositivo, principalmente de interés teórico, llamado tabla condicional (o tabla c) puede representar una respuesta de este tipo:
Nombre | Edad | condición |
---|---|---|
Harriet | ω 1 | ω 1 = 22 |
donde la columna de condición se interpreta como que la fila no existe si la condición es falsa. Resulta que debido a que las fórmulas en la columna de condición de una tabla c pueden ser fórmulas de lógica proposicional arbitrarias , un algoritmo para el problema de si una tabla c representa alguna relación concreta tiene una complejidad co-NP-completa , por lo que tiene poco valor práctico.
Por lo tanto, es deseable una noción más débil de representación. Imielinski y Lipski introdujeron la noción de representación débil , que esencialmente permite que las consultas (elevadas) sobre un constructo devuelvan una representación solo para información segura , es decir, si es válida para todas las instancias (modelos) del " mundo posible " del constructo. Concretamente, un constructo es un sistema de representación débil si
El lado derecho de la ecuación anterior es la información segura , es decir, la información que se puede extraer con certeza de la base de datos independientemente de los valores que se utilicen para reemplazar los valores nulos en la base de datos. En el ejemplo que consideramos anteriormente, es fácil ver que la intersección de todos los modelos posibles (es decir, la información segura) de la consulta de selección está realmente vacía porque, por ejemplo, la consulta (no elevada) no devuelve filas para la relación EmpH37. De manera más general, Imielinski y Lipski demostraron que las tablas Codd son un sistema de representación débil si el lenguaje de consulta se limita a proyecciones, selecciones (y cambio de nombre de columnas). Sin embargo, tan pronto como agregamos uniones al lenguaje de consulta, incluso esta propiedad débil se pierde, como se evidencia en la siguiente sección.WHERE Age = 22
Considere la siguiente consulta sobre la misma tabla Codd Emp de la sección anterior:
SELECCIONAR Nombre DE Emp DONDE Edad = 22 UNION SELECCIONAR Nombre DE Emp DONDE Edad <> 22 ;
Cualquiera que sea el valor concreto que uno elija para la NULL
edad de Harriet, la consulta anterior devolverá la columna completa de nombres de cualquier modelo de Emp , pero cuando la consulta (elevada) se ejecuta en Emp mismo, Harriet siempre faltará, es decir, tenemos:
Resultado de la consulta sobre Emp : |
| Resultado de la consulta sobre cualquier modelo de Emp : |
|
Por lo tanto, cuando se agregan uniones al lenguaje de consulta, las tablas Codd ni siquiera son un sistema de representación débil de la información faltante, lo que significa que las consultas sobre ellas ni siquiera informan toda la información segura . Es importante señalar aquí que la semántica de UNION en valores nulos, que se analiza en una sección posterior, ni siquiera entró en juego en esta consulta. La naturaleza "olvidadiza" de las dos subconsultas fue todo lo que se necesitó para garantizar que alguna información segura no se informara cuando se ejecutó la consulta anterior en la tabla Codd Emp.
En el caso de las uniones naturales , el ejemplo necesario para demostrar que es posible que alguna consulta no informe cierta información es un poco más complicado. Considere la tabla
F1 | F2 | F3 |
---|---|---|
11 | NULL | 13 |
21 | NULL | 23 |
31 | 32 | 33 |
y la consulta
SELECCIONAR F1 , F3 DE ( SELECCIONAR F1 , F2 DE J ) COMO F12 UNIÓN NATURAL ( SELECCIONAR F2 , F3 DE J ) COMO F23 ;
Resultado de la consulta en J: |
| Resultado de la consulta sobre cualquier modelo de J: |
|
La intuición de lo que sucede arriba es que las tablas Codd que representan las proyecciones en las subconsultas pierden de vista el hecho de que los valores nulos en las columnas F12.F2 y F23.F2 son en realidad copias de los originales en la tabla J. Esta observación sugiere que una mejora relativamente simple de las tablas Codd (que funciona correctamente para este ejemplo) sería usar constantes de Skolem (es decir, funciones de Skolem que también son funciones constantes ), digamos ω 12 y ω 22 en lugar de un solo símbolo NULL. Este enfoque, llamado tablas v o tablas Naive, es computacionalmente menos costoso que las tablas c discutidas arriba. Sin embargo, todavía no es una solución completa para información incompleta en el sentido de que las tablas v son solo una representación débil para consultas que no usan ninguna negación en la selección (y tampoco usan ninguna diferencia de conjuntos). El primer ejemplo considerado en esta sección usa una cláusula de selección negativa, , por lo que también es un ejemplo donde las consultas de tablas v no reportarían información segura.WHERE Age <> 22
El lugar principal en el que la lógica de tres valores de SQL se cruza con el lenguaje de definición de datos (DDL) de SQL es en la forma de restricciones de comprobación . Una restricción de comprobación colocada en una columna opera bajo un conjunto de reglas ligeramente diferente a las de la WHERE
cláusula DML. Mientras que una WHERE
cláusula DML debe evaluarse como Verdadero para una fila, una restricción de comprobación no debe evaluarse como Falso. (Desde una perspectiva lógica, los valores designados son Verdadero y Desconocido). Esto significa que una restricción de comprobación tendrá éxito si el resultado de la comprobación es Verdadero o Desconocido. La siguiente tabla de ejemplo con una restricción de comprobación prohibirá que se inserten valores enteros en la columna i , pero permitirá que se inserten valores Nulos, ya que el resultado de la comprobación siempre se evaluará como Desconocido para los valores Nulos. [19]
CREAR TABLA t ( i ENTERO , RESTRICCIÓN ck_i COMPROBAR ( i < 0 Y i = 0 Y i > 0 ) );
Debido al cambio en los valores designados en relación con la cláusula WHERE , desde una perspectiva lógica la ley del tercero excluido es una tautología para las restricciones CHECK , lo que significa que siempre tiene éxito. Además, suponiendo que los valores nulos se deben interpretar como valores existentes pero desconocidos, algunos CHECK patológicos como el anterior permiten la inserción de valores nulos que nunca podrían reemplazarse por ningún valor no nulo.CHECK (p OR NOT p)
Para restringir una columna para que rechace valores nulos, NOT NULL
se puede aplicar la restricción, como se muestra en el ejemplo siguiente. La NOT NULL
restricción es semánticamente equivalente a una restricción de verificación con un IS NOT NULL
predicado.
CREAR TABLA t ( i ENTERO NO NULO );
De manera predeterminada, las restricciones de verificación contra claves externas tienen éxito si alguno de los campos de dichas claves es nulo. Por ejemplo, la tabla
CREAR TABLA Libros ( título VARCHAR ( 100 ), apellido_autor VARCHAR ( 20 ), nombre_autor VARCHAR ( 20 ), CLAVE FORENA ( apellido_autor , nombre_autor ) REFERENCIAS Autores ( apellido , nombre ));
permitiría la inserción de filas donde author_last o author_first son NULL
independientemente de cómo se defina la tabla Authors o lo que contenga. Más precisamente, un valor nulo en cualquiera de estos campos permitiría cualquier valor en el otro, incluso si no se encuentra en la tabla Authors. Por ejemplo, si Authors contuviera solo ('Doe', 'John')
, entonces ('Smith', NULL)
satisfaría la restricción de clave externa. SQL-92 agregó dos opciones adicionales para limitar las coincidencias en tales casos. Si MATCH PARTIAL
se agrega después de la REFERENCES
declaración, entonces cualquier valor que no sea nulo debe coincidir con la clave externa, por ejemplo, ('Doe', NULL)
todavía coincidiría, pero ('Smith', NULL)
no. Finalmente, si MATCH FULL
se agrega entonces ('Doe', NULL)
tampoco coincidiría con la restricción, pero (NULL, NULL)
aún así coincidiría.
Las uniones externas de SQL , incluidas las uniones externas izquierdas, derechas y completas, generan automáticamente valores nulos como marcadores de posición para los valores faltantes en las tablas relacionadas. En el caso de las uniones externas izquierdas, por ejemplo, se generan valores nulos en lugar de las filas que faltan en la tabla y que aparecen en el lado derecho del LEFT OUTER JOIN
operador. El siguiente ejemplo simple utiliza dos tablas para demostrar la generación de marcadores de posición nulos en una unión externa izquierda.
La primera tabla ( Empleado ) contiene números de identificación y nombres de empleados, mientras que la segunda tabla ( Número de teléfono ) contiene números de identificación y números de teléfono de empleados relacionados , como se muestra a continuación.
|
|
La siguiente consulta SQL de ejemplo realiza una unión externa izquierda en estas dos tablas.
SELECCIONAR e . ID , e . Apellido , e . Nombre , pn . Número DE Empleado e UNIÓN EXTERNA IZQUIERDA NúmeroDeTeléfono pn EN e . ID = pn . ID ;
El conjunto de resultados generado por esta consulta demuestra cómo SQL utiliza Null como marcador de posición para los valores que faltan en la tabla de la derecha ( PhoneNumber ), como se muestra a continuación.
IDENTIFICACIÓN | Apellido | Nombre de pila | Número |
---|---|---|---|
1 | Johnson | José | 555-2323 |
2 | Ametralladora | Larry | NULL |
3 | Thompson | Tomás | 555-9876 |
4 | Patterson | Patricia | NULL |
SQL define funciones de agregación para simplificar los cálculos de agregación de datos del lado del servidor. A excepción de la COUNT(*)
función, todas las funciones de agregación realizan un paso de eliminación de valores nulos, de modo que los valores nulos no se incluyen en el resultado final del cálculo. [20]
Tenga en cuenta que la eliminación de Null no es equivalente a reemplazar Null por cero. Por ejemplo, en la siguiente tabla, AVG(i)
(el promedio de los valores de i
) dará un resultado diferente al de AVG(j)
:
i | yo |
---|---|
150 | 150 |
200 | 200 |
250 | 250 |
NULL | 0 |
Aquí AVG(i)
tenemos 200 (el promedio de 150, 200 y 250), mientras que AVG(j)
es 150 (el promedio de 150, 200, 250 y 0). Un efecto secundario bien conocido de esto es que en SQL, AVG(z)
no es equivalente a SUM(z)/COUNT(*)
sino a SUM(z)/COUNT(z)
. [4]
La salida de una función agregada también puede ser nula. A continuación se muestra un ejemplo:
SELECCIONAR CONTAR ( * ), MIN ( e . Salario ), MAX ( e . Salario ) DE Empleado e DONDE e . Apellido COMO '%Jones%' ;
Esta consulta siempre generará exactamente una fila, contando el número de empleados cuyo apellido contiene "Jones", y brindando el salario mínimo y máximo encontrado para esos empleados. Sin embargo, ¿qué sucede si ninguno de los empleados cumple con los criterios dados? Calcular el valor mínimo o máximo de un conjunto vacío es imposible, por lo que esos resultados deben ser NULL, lo que indica que no hay respuesta. Este no es un valor desconocido, es un valor nulo que representa la ausencia de un valor. El resultado sería:
CONTAR(*) | MIN(salario electrónico) | MAX(e.Salario) |
---|---|---|
0 | NULL | NULL |
Debido a que SQL:2003 define todos los marcadores Null como desiguales entre sí, se requirió una definición especial para agrupar los Null al realizar ciertas operaciones. SQL define "cualquier par de valores que sean iguales entre sí, o cualesquiera dos Null", como "no distintos". [21] Esta definición de no distinto permite a SQL agrupar y ordenar los Null cuando GROUP BY
se utiliza la cláusula (u otra característica del lenguaje SQL que realiza la agrupación).
Otras operaciones, cláusulas y palabras clave de SQL que utilizan la definición "no distinto" en su tratamiento de valores nulos incluyen:
PARTITION BY
cláusula de las funciones de clasificación y ventanas comoROW_NUMBER
UNION
, INTERSECT
, y EXCEPT
, que tratan los valores NULL como iguales para fines de comparación/eliminación de filasDISTINCT
palabra clave utilizada en SELECT
las consultasEl principio de que los valores nulos no son iguales entre sí (sino que el resultado es desconocido) se viola de manera efectiva en la especificación SQL para el UNION
operador, que identifica los valores nulos entre sí. [1] En consecuencia, algunas operaciones de conjuntos en SQL, como la unión y la diferencia, pueden producir resultados que no representan información segura, a diferencia de las operaciones que involucran comparaciones explícitas con valores nulos (por ejemplo, las de una WHERE
cláusula analizada anteriormente). En la propuesta de Codd de 1979 (que fue adoptada por SQL92) esta inconsistencia semántica se racionaliza argumentando que la eliminación de duplicados en las operaciones de conjuntos ocurre "en un nivel de detalle más bajo que las pruebas de igualdad en la evaluación de las operaciones de recuperación". [11]
El estándar SQL no define explícitamente un orden de clasificación predeterminado para los valores nulos. En cambio, en los sistemas que lo cumplen, los valores nulos se pueden ordenar antes o después de todos los valores de datos mediante el uso de las cláusulas NULLS FIRST
o de la lista, respectivamente. Sin embargo, no todos los proveedores de DBMS implementan esta funcionalidad. Los proveedores que no implementan esta funcionalidad pueden especificar diferentes tratamientos para la ordenación de valores nulos en el DBMS. [19]NULLS LAST
ORDER BY
Algunos productos SQL no indexan claves que contienen valores NULL. Por ejemplo, las versiones de PostgreSQL anteriores a la 8.3 no lo hacían, y la documentación para un índice de árbol B indica que [22]
Los árboles B pueden manejar consultas de igualdad y de rango sobre datos que se pueden ordenar de alguna manera. En particular, el planificador de consultas de PostgreSQL considerará el uso de un índice de árbol B siempre que una columna indexada esté involucrada en una comparación que utilice uno de estos operadores: < ≤ = ≥ >
También se pueden implementar construcciones equivalentes a combinaciones de estos operadores, como BETWEEN e IN, con una búsqueda de índice de árbol B. (Pero tenga en cuenta que IS NULL no es equivalente a = y no es indexable).
En los casos en los que el índice exige unicidad, los valores NULL se excluyen del índice y no se exige unicidad entre valores NULL. Nuevamente, citando la documentación de PostgreSQL : [23]
Cuando se declara un índice único, no se permitirán varias filas de tabla con valores indexados iguales. Los valores nulos no se consideran iguales. Un índice único de varias columnas solo rechazará los casos en los que todas las columnas indexadas sean iguales en dos filas.
Esto es coherente con el comportamiento definido en SQL:2003 de las comparaciones escalares Null.
Otro método para indexar valores nulos implica tratarlos como no distintos de acuerdo con el comportamiento definido en SQL:2003. Por ejemplo, la documentación de Microsoft SQL Server indica lo siguiente: [24]
Para fines de indexación, los valores NULL se comparan como iguales. Por lo tanto, no se puede crear un índice único o una restricción UNIQUE si las claves son NULL en más de una fila. Seleccione columnas que estén definidas como NOT NULL cuando se elijan columnas para un índice único o una restricción única.
Ambas estrategias de indexación son coherentes con el comportamiento de los valores nulos definido en SQL:2003. Dado que las metodologías de indexación no están definidas explícitamente por el estándar SQL:2003, el diseño y la implementación de las estrategias de indexación para los valores nulos queda en manos de los proveedores.
SQL define dos funciones para manejar explícitamente los valores nulos: NULLIF
y COALESCE
. Ambas funciones son abreviaturas de expresiones buscadasCASE
. [25]
La NULLIF
función acepta dos parámetros. Si el primer parámetro es igual al segundo, NULLIF
devuelve Null. De lo contrario, se devuelve el valor del primer parámetro.
NULLIF ( valor1 , valor2 )
Por tanto, NULLIF
es una abreviatura de la siguiente CASE
expresión:
CASO CUANDO valor1 = valor2 ENTONCES NULO DE LO CONTRARIO valor1 FIN
La COALESCE
función acepta una lista de parámetros y devuelve el primer valor no nulo de la lista:
COALESCE ( valor1 , valor2 , valor3 , ...)
COALESCE
se define como una abreviatura de la siguiente CASE
expresión SQL:
CASO CUANDO valor1 NO ES NULO ENTONCES valor1 CUANDO valor2 NO ES NULO ENTONCES valor2 CUANDO valor3 NO ES NULO ENTONCES valor3 ... FIN
Algunos DBMS SQL implementan funciones específicas del proveedor similares a COALESCE
. Algunos sistemas (por ejemplo, Transact-SQL ) implementan una ISNULL
función u otras funciones similares que son funcionalmente similares a COALESCE
. (Consulte Is
funciones para obtener más información sobre las IS
funciones en Transact-SQL).
La NVL
función Oracle acepta dos parámetros. Devuelve el primer parámetro que no sea NULL o NULL si todos los parámetros son NULL.
Una COALESCE
expresión se puede convertir en una NVL
expresión equivalente de la siguiente manera:
COALESCE ( val1 , ... , val { n } )
se convierte en:
NVL ( val1 , NVL ( val2 , NVL ( val3 , … , NVL ( val { n - 1 } , val { n } ) … )))
Un caso de uso de esta función es reemplazar en una expresión un NULL por un valor como en NVL(SALARY, 0)
el que dice, 'si SALARY
es NULL, reemplácelo con el valor 0'.
Sin embargo, hay una excepción notable. En la mayoría de las implementaciones, COALESCE
evalúa sus parámetros hasta que llega al primer parámetro que no sea NULL, mientras que NVL
evalúa todos sus parámetros. Esto es importante por varias razones. Un parámetro después del primer parámetro que no sea NULL podría ser una función, lo que podría ser costoso en términos computacionales, inválido o podría crear efectos secundarios inesperados.
El NULL
literal no tiene tipo en SQL, lo que significa que no está designado como un entero, un carácter ni ningún otro tipo de datos específico . [26] Debido a esto, a veces es obligatorio (o deseable) convertir explícitamente los valores nulos en un tipo de datos específico. Por ejemplo, si el RDBMS admite funciones sobrecargadas , SQL podría no poder resolver automáticamente la función correcta sin conocer los tipos de datos de todos los parámetros, incluidos aquellos para los que se pasa un valor nulo.
Es posible realizar la conversión de un NULL
literal a un valor nulo de un tipo específico mediante el uso de la CAST
función introducida en SQL-92 . Por ejemplo:
CONVERTIR ( NULO COMO ENTERO )
representa un valor ausente de tipo INTEGER.
El tipo real de Unknown (distinto o no del propio NULL) varía entre las implementaciones de SQL. Por ejemplo, lo siguiente
SELECCIONAR 'ok' DONDE ( NULL <> 1 ) ES NULO ;
analiza y ejecuta con éxito en algunos entornos (por ejemplo, SQLite o PostgreSQL ) que unifican un booleano NULL con Unknown pero no puede analizar en otros (por ejemplo, en SQL Server Compact ). MySQL se comporta de manera similar a PostgreSQL en este sentido (con la pequeña excepción de que MySQL considera que TRUE y FALSE no son diferentes de los enteros ordinarios 1 y 0). PostgreSQL implementa adicionalmente un IS UNKNOWN
predicado, que se puede usar para probar si un resultado lógico de tres valores es Unknown, aunque esto es meramente azúcar sintáctico.
El estándar ISO SQL:1999 introdujo el tipo de datos BOOLEAN en SQL, sin embargo todavía es sólo una característica opcional, no esencial, codificada como T031. [27]
Cuando está restringido por una NOT NULL
restricción, el tipo de datos BOOLEAN de SQL funciona como el tipo Boolean de otros lenguajes. Sin embargo, sin restricciones, el tipo de datos BOOLEAN, a pesar de su nombre, puede contener los valores verdaderos TRUE, FALSE y UNKNOWN, todos los cuales se definen como literales Booleanos según el estándar. El estándar también afirma que NULL y UNKNOWN "pueden usarse indistintamente para significar exactamente lo mismo". [28] [29]
El tipo booleano ha sido objeto de críticas, particularmente debido al comportamiento obligatorio del literal UNKNOWN, que nunca es igual a sí mismo debido a la identificación con NULL. [30]
Como se mencionó anteriormente, en la implementación PostgreSQL de SQL , se utiliza Null para representar todos los resultados UNKNOWN, incluido el UNKNOWN BOOLEAN. PostgreSQL no implementa el literal UNKNOWN (aunque sí implementa el operador IS UNKNOWN, que es una característica ortogonal). La mayoría de los demás proveedores importantes no admiten el tipo Boolean (como se define en T031) a partir de 2012. [31] Sin embargo, la parte procedimental de PL/SQL de Oracle admite variables BOOLEAN; a estas también se les puede asignar NULL y el valor se considera el mismo que UNKNOWN. [32]
La falta de comprensión de cómo funciona Null es la causa de una gran cantidad de errores en el código SQL, tanto en las sentencias SQL estándar ISO como en los dialectos SQL específicos compatibles con los sistemas de gestión de bases de datos del mundo real. Estos errores suelen ser el resultado de la confusión entre Null y 0 (cero) o una cadena vacía (un valor de cadena con una longitud de cero, representado en SQL como ''
). Sin embargo, el estándar SQL define a Null como diferente tanto de una cadena vacía como del valor numérico 0
. Mientras que Null indica la ausencia de cualquier valor, la cadena vacía y el cero numérico representan valores reales.
Un error clásico es el intento de utilizar el operador igual =
en combinación con la palabra clave NULL
para buscar filas con valores nulos. Según el estándar SQL, esta es una sintaxis no válida y debe generar un mensaje de error o una excepción. Pero la mayoría de las implementaciones aceptan la sintaxis y evalúan dichas expresiones como UNKNOWN
. La consecuencia es que no se encuentran filas, independientemente de si existen filas con valores nulos o no. La forma propuesta de recuperar filas con valores nulos es el uso del predicado IS NULL
en lugar de = NULL
.
SELECT * FROM sometable WHERE num = NULL ; -- Debe ser "DONDE num ES NULO"
En un ejemplo relacionado, pero más sutil, una WHERE
cláusula o una declaración condicional podría comparar el valor de una columna con una constante. A menudo se supone incorrectamente que un valor faltante sería "menor que" o "no igual a" una constante si ese campo contiene Null, pero, de hecho, dichas expresiones devuelven Unknown. A continuación se muestra un ejemplo:
SELECT * FROM sometable WHERE num <> 1 ; -- Las filas donde num es NULL no se devolverán, -- contrariamente a las expectativas de muchos usuarios.
Estas confusiones surgen porque la ley de identidad está restringida en la lógica de SQL. Cuando se trata de comparaciones de igualdad utilizando el NULL
literal o el UNKNOWN
valor de verdad, SQL siempre devolverá UNKNOWN
como resultado la expresión. Esta es una relación de equivalencia parcial y convierte a SQL en un ejemplo de lógica no reflexiva . [33]
De manera similar, los valores nulos suelen confundirse con cadenas vacías. Considere la LENGTH
función que devuelve la cantidad de caracteres en una cadena. Cuando se pasa un valor nulo a esta función, la función devuelve un valor nulo. Esto puede generar resultados inesperados si los usuarios no están familiarizados con la lógica de 3 valores. A continuación, se muestra un ejemplo:
SELECT * FROM sometable WHERE LENGTH ( string ) < 20 ; -- No se devolverán las filas donde la cadena sea NULL.
Esto se complica por el hecho de que en algunos programas de interfaz de base de datos (o incluso implementaciones de bases de datos como la de Oracle), NULL se informa como una cadena vacía, y las cadenas vacías pueden almacenarse incorrectamente como NULL.
La implementación de Null en ISO SQL es objeto de críticas, debates y pedidos de cambio. En The Relational Model for Database Management: Version 2 , Codd sugirió que la implementación de Null en SQL era defectuosa y debería ser reemplazada por dos marcadores de tipo Null distintos. Los marcadores que propuso debían representar "Falta pero aplicable" y "Falta pero inaplicable" , conocidos como valores A y valores I , respectivamente. La recomendación de Codd, si se hubiera aceptado, habría requerido la implementación de una lógica de cuatro valores en SQL. [5] Otros han sugerido agregar marcadores de tipo Null adicionales a la recomendación de Codd para indicar aún más razones por las que un valor de datos podría estar "Falta", lo que aumenta la complejidad del sistema lógico de SQL. En varias ocasiones, también se han presentado propuestas para implementar múltiples marcadores Null definidos por el usuario en SQL. Debido a la complejidad de los sistemas de manejo de Null y de lógica necesarios para admitir múltiples marcadores Null, ninguna de estas propuestas ha obtenido una aceptación generalizada.
Chris Date y Hugh Darwen , autores de The Third Manifesto , han sugerido que la implementación de SQL Null es inherentemente defectuosa y debería eliminarse por completo, [34] señalando inconsistencias y fallas en la implementación del manejo de SQL Null (particularmente en funciones agregadas) como prueba de que todo el concepto de Null es defectuoso y debería eliminarse del modelo relacional. [35] Otros, como el autor Fabian Pascal , han manifestado su creencia de que "la forma en que el cálculo de la función debe tratar los valores faltantes no está gobernada por el modelo relacional". [ cita requerida ]
Otro punto de conflicto en relación con los valores nulos es que violan el modelo de supuesto de mundo cerrado de las bases de datos relacionales al introducir en él un supuesto de mundo abierto . [36] El supuesto de mundo cerrado, en lo que respecta a las bases de datos, establece que "todo lo que establece la base de datos, ya sea explícita o implícitamente, es verdadero; todo lo demás es falso". [37] Esta visión supone que el conocimiento del mundo almacenado en una base de datos es completo. Los valores nulos, sin embargo, operan bajo el supuesto de mundo abierto, en el que algunos elementos almacenados en la base de datos se consideran desconocidos, lo que hace que el conocimiento del mundo almacenado en la base de datos sea incompleto.
{{cite web}}
: Falta o está vacío |url=
( ayuda )