PostgreSQL 'NOT IN' et sous-requête

89

J'essaye d'exécuter cette requête:

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

Mais je n'obtiens aucun résultat. Je l'ai testé, et je sais qu'il y a un problème avec la syntaxe. Dans MySQL, une telle requête fonctionne parfaitement. J'ai ajouté une ligne pour être sûr qu'il y en a une macqui n'existe pas dans le consolstableau, mais qui ne donne toujours aucun résultat.

skowron-ligne
la source
4
Est la consols.maccolonne NULLou NOT NULL?
Mark Byers

Réponses:

167

Lorsque vous utilisez NOT IN, vous devez vous assurer qu'aucune des valeurs n'est 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
la source
4
Remarque: la WHERE mac IS NOT NULLclause dans la sous-requête n'est pas nécessaire, car elle In(...)supprime toujours les NULL (et les doublons). Parce qu'un ensemble ne peut pas contenir de valeurs nulles
wildplasser
7
@wildplasser Je ne sais pas à ce sujet. Cela ne fonctionnait pas pour moi, jusqu'à ce que j'ajoute le IS NOT NULL. Le niché en SELECTretournait quelques-uns NULLS, et cela faisait trébucher le IN(SELECT...).
robins35
2
J'apprécierais beaucoup une explication sur les raisons pour lesquelles IS NOT NULLcela fonctionne.
mbarkhau
7
Il semble que l'utilisation NULLdans une NOT INclause ne fonctionne pas car une comparaison avec NULLn'est ni vraie ni fausse. sqlbadpractices.com/using-not-in-operator-with-null-values
mbarkhau
2
La requête ne renverra aucune ligne en l'absence de is not nullsi la sous-requête ne produit aucune valeur correspondante et au moins une nullvaleur. De la section 9.22 du manuel PostgreSQL actuel (version 10): "[…] s'il n'y a pas de valeurs égales à droite et qu'au moins une ligne de droite donne null, le résultat de la construction NOT IN sera nul, pas vrai . "
Christopher Lewis
29

Lorsque vous utilisez NOT IN, vous devez également considérer NOT EXISTS, qui gère les cas nuls en silence. Voir aussi Wiki 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
la source
3
Notez également une énorme perte de performances lors de l'utilisation de NOT EXISTSvs... NOT IN
IcanDividePar0
1
@ IcanDivideBy0 Dans la plupart des cas, ils génèrent le même plan de requête. L'avez-vous testé?
wildplasser
1
Il y a aussi un gain de performances en utilisant NOT IN au lieu de NOT EXISTS, en cas de sous-requêtes. Voir wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_NOT_IN
Gerbrand
8

Vous pouvez également utiliser une condition LEFT JOIN et 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 index sur les colonnes "mac" peut améliorer les performances.

Frank Heikens
la source