Seguro que a todos nos ha pasado que por culpa de valores NULL en nuestras tablas obtenemos resultados “raros” o no esperados cuando ejecutamos una consulta que, a priori, puede ser simple. Al final acabas volviéndote loco hasta que te das cuenta que la columna en cuestión tiene valores nulos.

Escribo esto porque es una cosa que me pasó hace poco y es el típico despiste que se puede evitar si tienes en cuenta como gestiona SQL Server los valores NULL.

En mi caso fue una consulta muy simple con la cláusula NOT IN, se trataba de encontrar todas las filas de una tabla cuyo ID no se encontraba en otra tabla, y para mi sorpresa la consulta no devolvía filas mientras que si usaba la cláusula EXCEPTla consulta me devolvía las filas que yo estaba esperando.

Vamos a representar eso con un ejemplo para verlo mucho más claro, imaginemos que tenemos una tabla de Usuarios y una tabla de Publicaciones y queremos saber qué usuarios no han escrito ninguna publicación.

Primero, vamos a crear un par de tablas sencillas y vamos a insertar unos datos de ejemplo:

CREATE TABLE USUARIOS (
ID_USUARIO INT,
NOMBRE VARCHAR(50)
)

CREATE TABLE PUBLICACIONES (
ID_PUBLICACION INT,
ID_USUARIO INT,
TITULO VARCHAR(50)
)

INSERT INTO USUARIOS VALUES
(1, 'Fernando'),
(2, 'Ana'),
(3, 'Jose'),
(4, 'María')

INSERT INTO PUBLICACIONES VALUES
(1, 1, 'Publicación 1'),
(2, 2, 'Publicación 2'),
(3, 3, 'Publicación 3')

datos ejemplo

Vemos que tenemos 4 usuarios y 3 publicaciones, y vemos también que María con el ID número 4 no ha escrito ninguna publicación, por lo tanto si realizamos la siguiente consulta, el resultado tendría que ser María:

SELECT ID_USUARIO, NOMBRE
FROM USUARIOS
WHERE ID_USUARIO NOT IN (SELECT ID_USUARIO FROM PUBLICACIONES)

SELECT ID_USUARIO FROM USUARIOS
EXCEPT
SELECT ID_USUARIO FROM PUBLICACIONES

resul eje 1

Y efectivamente obtenemos el resultado esperado.

En la primera consulta se piden los ID_USUARIO que no se encuentran en la tabla de publicaciones con la cláusula NOT IN, mientras que en la segunda consulta utilizamos la cláusula EXCEPT, es decir, los IDs que tenemos en la primera tabla (Usuarios) EXCEPTO los IDs que tenemos en la segunda (Publicaciones)

Pero qué pasa si insertamos un valor NULL en la tabla de Publicaciones:

INSERT INTO PUBLICACIONES VALUES
(NULL, NULL, NULL)

datos nulo

Realizamos las mismas consultas anteriores y este es el resultado:

resul eje 2

Como se puede ver la consulta del EXCEPTfunciona y me devuelve el ID número 4 pero la primera consulta que usa el NOT IN no devuelve nada.

¿Por qué? Pues esto se produce por la manera que tiene SQL Server de tratar los valores nulos. Para SQL Server cuando tenemos ANSI_NULLS en ON estamos haciendo la siguiente comparación:

4 <> 1 and 4<>2 and 4<>3 and 4<>NULL

Todas se evalúan a verdadero menos la comparación con NULL que se evalúa como UNKNOWN o desconocido, esto hace que el predicado se evalúe como UNKNOWN y no obtenemos ninguna fila.

Esta es la lógica que SQL Server utiliza para el valor UNKNOWN:

UNKNOWN AND TRUE = UNKNOWN

UNKNOWN OR TRUE = TRUE

UNKNOWN OR FALSE = UNKNOWN

Existen varias soluciones para solventar esta problemática, una de ellas es poner ANSI_NULL a OFF:

SET ANSI_NULLS OFF

SELECT ID_USUARIO, NOMBRE
FROM USUARIOS
WHERE ID_USUARIO NOT IN (SELECT ID_USUARIO FROM PUBLICACIONES)
Otra de ella sería excluir los valores nulos de la sub consulta:SELECT ID_USUARIO, NOMBRE
FROM USUARIOS
WHERE ID_USUARIO NOT IN (SELECT ID_USUARIO FROM PUBLICACIONES WHERE ID_USUARIO IS NOT NULL)

resul eje 3

Espero que este post os haya servido de ayuda para entender un poquito más como SQL Server trata los valores NULOS en los predicados y que tengáis en cuenta estos detalles a la hora de realizar vuestras consultas. carita sonriente

Para cualquier consulta o duda podéis contactar conmigo mediante:

cbernabeu@solidq.com

@CarminaBH

 

 

 

0 Shares:
1 comment
Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

You May Also Like