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.


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
)

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
  );

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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.