PostgreSQL 'NOT IN' y subconsulta

89

Estoy tratando de ejecutar esta consulta:

SELECT mac, creation_date 
FROM logs 
WHERE logs_type_id=11
AND mac NOT IN (select consols.mac from consols)

Pero no obtengo resultados. Lo probé y sé que hay algún problema con la sintaxis. En MySQL, esta consulta funciona perfectamente. Agregué una fila para asegurarme de que hay una macque no existe en la consolstabla, pero aún así no da ningún resultado.

skowron-line
fuente
4
¿Es la consols.maccolumna NULLo NOT NULL?
Mark Byers

Respuestas:

167

Cuando use NOT IN, debe asegurarse de que ninguno de los valores sea NULL:

SELECT mac, creation_date 
FROM logs 
WHERE logs_type_id=11
AND mac NOT IN (
    SELECT mac
    FROM consols
    WHERE mac IS NOT NULL -- add this
)
Mark Byers
fuente
4
Nota: la WHERE mac IS NOT NULLcláusula en la subconsulta no es necesaria, ya que In(...)siempre elimina NULL (y duplicados). Porque un conjunto no puede contener valores NULL
wildplasser
7
@wildplasser No sé sobre eso. No funcionó para mí, hasta que agregué el IS NOT NULL. El anidado SELECTestaba volviendo unos pocos NULLS, y eso fue tropezando el IN(SELECT...).
robins35
2
Agradecería mucho una explicación de por qué IS NOT NULLesto funciona.
mbarkhau
7
Parece que usar NULLen una NOT INcláusula no funciona porque una comparación con NULLno es ni verdadera ni falsa. sqlbadpractices.com/using-not-in-operator-with-null-values
mbarkhau
2
La consulta no devolverá filas en ausencia de is not nullsi la subconsulta no produce valores coincidentes y al menos un nullvalor. De la sección 9.22 del manual de PostgreSQL actual (versión 10): "[…] si no hay valores iguales a la derecha y al menos una fila de la derecha da un resultado nulo, el resultado de la construcción NOT IN será nulo, no verdadero . "
Christopher Lewis
29

Cuando use NOT IN, también debe considerar NOT EXISTS, que maneja los casos nulos en silencio. Véase también Wiki de PostgreSQL

SELECT mac, creation_date 
FROM logs lo
WHERE logs_type_id=11
AND NOT EXISTS (
  SELECT *
  FROM consols nx
  WHERE nx.mac = lo.mac
  );
wildplasser
fuente
3
También tenga en cuenta una gran pérdida de rendimiento cuando se usa NOT EXISTSvs... NOT IN
IcanDivideBy0
1
@ IcanDivideBy0 En la mayoría de los casos, generan el mismo plan de consulta. ¿Lo has probado?
wildplasser
1
También hay una ganancia de rendimiento al usar NOT IN en lugar de NOT EXISTS, en el caso de subconsultas. Ver wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_NOT_IN
Gerbrand
8

También puede utilizar una condición LEFT JOIN y IS NULL:

SELECT 
  mac, 
  creation_date 
FROM 
  logs
    LEFT JOIN consols ON logs.mac = consols.mac
WHERE 
  logs_type_id=11
AND
  consols.mac IS NULL;

Un índice en las columnas "mac" podría mejorar el rendimiento.

Frank Heikens
fuente