¿Por qué no puedo usar valores nulos en combinaciones?


13

He resuelto el problema de la consulta usando ... row_number() over (partition by... esta es una pregunta más general sobre por qué no podemos usar columnas con valores nulos en combinaciones. ¿Por qué un nulo no puede ser igual a un nulo por el bien de una unión?

Respuestas:


31

¿Por qué un nulo no puede ser igual a un nulo por el bien de una unión?

Solo dile a Oracle que haga eso:

select *
from one t1 
  join two t2 on coalesce(t1.id, -1) = coalesce(t2.id, -1);

(Tenga en cuenta que en SQL estándar podría usar t1.id is not distinct from t2.idpara obtener un operador de igualdad nulo-seguro, pero Oracle no lo admite)

Pero esto solo funcionará si el valor de reemplazo (-1 en el ejemplo anterior) en realidad no aparece en la tabla. Puede ser posible encontrar un valor "mágico" para los números , pero será muy difícil para los valores de los caracteres (especialmente porque Oracle también trata una cadena vacía null)

Además: no idse utilizará ningún índice en las columnas ( aunque podría definir un índice basado en funciones con la coalesce()expresión).

Otra opción que funciona para todos los tipos, sin valores mágicos:

              on t1.id = t2.id or (t1.id is null and t2.id is null)

Pero la verdadera pregunta es: ¿tiene sentido?

Considere los siguientes datos de muestra:

Tabla uno

id
----
1
2
(null)
(null)

Tabla dos

id
----
1
2
(null)
(null)
(null)

¿Cuál de la combinación de valores nulos se debe elegir en la unión? El ejemplo anterior dará como resultado una unión cruzada para todos los valores nulos.

T1_ID  | T2_ID 
-------+-------
     1 |      1
     2 |      2
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)

6

Alternativamente, puede hacer que dos nulos coincidan entre sí utilizando INTERSECTcomo operador de igualdad:

SELECT
  *
FROM
  t1
  INNER JOIN t2
    ON EXISTS (SELECT t1.ID FROM DUAL INTERSECT SELECT t2.ID FROM DUAL)
;

Vea esta demostración de DBFiddle para una ilustración.

Por supuesto, esto parece bastante bocado, aunque en realidad no es mucho más largo que la sugerencia de BriteSponge . Sin embargo, ciertamente no es una coincidencia, si perdona el juego de palabras, con la concisión de lo mencionado anteriormente en los comentarios de manera estándar, que es el IS NOT DISTINCT FROMoperador, que aún no es compatible con Oracle.


2

Solo para completar, mencionaré que la función SYS_OP_MAP_NONNULLahora se puede usar de manera segura para comparar valores que son nulos, ya que ahora está documentado en la documentación de 12c. Esto significa que Oracle no solo lo eliminará al azar y romperá su código.

SELECT *
FROM   one t1 
       JOIN two t2
         ON SYS_OP_MAP_NONNULL(t1.id) = SYS_OP_MAP_NONNULL(t2.id)

La ventaja es que no te encuentras con el problema del número 'mágico'.

La referencia en los documentos de Oracle está en Vistas materializadas básicas: elección de índices para vistas materializadas .


Entonces, ¿está documentado ahora? Porque AskTom (en 2003) declaró: " - está indocumentado y, por lo tanto, representa un riesgo de desaparecer o cambiar la funcionalidad, lo cual es suficiente para hacer que la gente simplemente" deje de leer "y que esté realmente enojado en la próxima versión. la única forma CORRECTA es: where (a = b or (a is null and b is null)) punto. Ese es mi pensamiento al respecto. No consideraría usarlo sys_op_map_nonnull, ignorar a ese hombre detrás de la cortina ".
ypercubeᵀᴹ

Si tiene un enlace, agréguelo a la pregunta. No he encontrado mención en 12c Funciones, pero buscar documentación de Oracle y una versión específica es bastante difícil.
ypercubeᵀᴹ

2

Puede unir valores nulos con decodificación:

on decode(t1.id, t2.id, 1, 0) = 1

decodetrata los nulos como iguales, por lo que esto funciona sin números "mágicos". Las dos columnas deben tener el mismo tipo de datos.

No hará el código más legible, pero probablemente aún mejor que t1.id = t2.id or (t1.id is null and t2.id is null)


1

¿Por qué no puedes usar valores nulos en uniones? En Oracle, los dos siguientes no se evalúan como verdaderos:

  • NULL = NULL
  • NULL <> NULL

Es por eso que tenemos IS NULL/ IS NOT NULLpara comprobar si hay valores nulos.
Para probar esto, simplemente puede hacer:

SELECT * FROM table_name WHERE NULL = NULL

Las uniones están evaluando una condición booleana, y no las programaron para operar de manera diferente. Puede poner un signo mayor que en la condición de unión y agregar otras condiciones; solo lo evalúa como una expresión booleana.

Supongo que un nulo no puede ser igual a un nulo en uniones por razones de consistencia. Desafiaría el comportamiento habitual del operador de comparación.


NULL = anythingresulta NULLporque el estándar SQL lo dice. Una fila satisface la condición de unión solo si la expresión es verdadera.
Laurenz Albe

1
Más allá del detalle de implementación literal (que no siempre es el caso: algunos DB tienen la opción de equiparar NULL a NULL para algunos / todos los propósitos) hay una razón lógica: NULL es desconocido. Cuando compara NULL con NULL, está preguntando "¿esta cosa desconocida es igual a esta otra cosa desconocida?", A la que la única respuesta razonable es "desconocido": otro NULL (que se asigna a falso en una situación de comparación).
David Spillett

-4

Un valor nulo en la mayoría de las bases de datos relacionales se considera DESCONOCIDO. No debe confundirse con todos los ceros HEX. si algo contiene nulo (desconocido), no puede compararlo.

Unknown = Known False
Unknown = Unknown False
Unknown >= Known False
Known >= Unknown False

Lo que significa que siempre que tenga un nulo como operando en una expresión booleana, la parte else siempre será verdadera.

Al contrario del odio general hacia nulo por parte de los desarrolladores, nulo tiene su lugar. Si algo es desconocido, use nulo.


66
En realidad, todas las comparaciones de ejemplo que tienes, rinden UNKNOWN, no FALSE;)
ypercubeᵀᴹ

Tienes razón, sin embargo, el propósito de una expresión booleana es dar como resultado verdadero o falso solamente, así que no nos volvamos locos aquí :).
jujiro
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.