Sélectionnez des lignes qui ne sont pas présentes dans une autre table

173

J'ai deux tables postgresql:

table name     column names
-----------    ------------------------
login_log      ip | etc.
ip_location    ip | location | hostname | etc.

Je veux obtenir chaque adresse IP à partir de login_loglaquelle il n'y a pas de ligne ip_location.
J'ai essayé cette requête mais cela génère une erreur de syntaxe.

SELECT login_log.ip 
FROM login_log 
WHERE NOT EXIST (SELECT ip_location.ip
                 FROM ip_location
                 WHERE login_log.ip = ip_location.ip)
ERROR: syntax error at or near "SELECT"
LINE 3: WHERE NOT EXIST (SELECT ip_location.ip`

Je me demande également si cette requête (avec des ajustements pour qu'elle fonctionne) est la requête la plus performante à cet effet.

stUrb
la source

Réponses:

387

Il existe essentiellement 4 techniques pour cette tâche, toutes en SQL standard.

NOT EXISTS

Souvent le plus rapide à Postgres.

SELECT ip 
FROM   login_log l 
WHERE  NOT EXISTS (
   SELECT  -- SELECT list mostly irrelevant; can just be empty in Postgres
   FROM   ip_location
   WHERE  ip = l.ip
   );

Considérez également:

LEFT JOIN / IS NULL

Parfois, c'est le plus rapide. Souvent le plus court. Résultat souvent le même plan de requête que NOT EXISTS.

SELECT l.ip 
FROM   login_log l 
LEFT   JOIN ip_location i USING (ip)  -- short for: ON i.ip = l.ip
WHERE  i.ip IS NULL;

EXCEPT

Court. Pas aussi facilement intégré dans des requêtes plus complexes.

SELECT ip 
FROM   login_log

EXCEPT ALL  -- "ALL" keeps duplicates and makes it faster
SELECT ip
FROM   ip_location;

Notez que ( par documentation ):

les doublons sont éliminés sauf s'ils EXCEPT ALLsont utilisés.

En règle générale, vous aurez besoin du ALLmot - clé. Si vous ne vous inquiétez pas, utilisez toujours parce qu'il fait la requête plus rapide .

NOT IN

Seulement bon sans NULLvaleurs ou si vous savez gérer NULLcorrectement. Je ne l' utiliserais pas à cette fin. De plus, les performances peuvent se détériorer avec des tables plus grandes.

SELECT ip 
FROM   login_log
WHERE  ip NOT IN (
   SELECT DISTINCT ip  -- DISTINCT is optional
   FROM   ip_location
   );

NOT INporte un "piège" pour les NULLvaleurs de chaque côté:

Question similaire sur dba.SE ciblée sur MySQL:

Erwin Brandstetter
la source
2
Quel SQL s'exécuterait plus rapidement étant donné que les volumes de données sont élevés dans les deux tables. (en supposant en milliards)
Teja
SAUF TOUT a été le plus rapide pour moi
Dan Parker
Soyez prudent avec LEFT JOIN- s'il y a plusieurs lignes correspondantes dans la table de recherche, cela créera une entrée en double dans votre requête principale pour chaque ligne correspondante, ce qui peut ne pas être souhaité.
Matthias Fripp le
@MatthiasFripp: Sauf que cela ne peut jamais se produire avec WHERE i.ip IS NULL, ce qui signifie qu'il n'y a aucune correspondance.
Erwin Brandstetter le
@ erwin-brandstetter: Bon point. Je me suis trébuché en pensant à la possibilité de plusieurs matches positifs, mais bien sûr, ceux-ci seraient tous exclus.
Matthias Fripp
2

A.) La commande N'EST PAS EXISTE, il vous manque le «S».

B.) Utilisez NOT IN à la place

SELECT ip 
  FROM login_log 
  WHERE ip NOT IN (
    SELECT ip
    FROM ip_location
  )
;
caleb.breckon
la source
4
NOT IN sur les grands ensembles de données est une idée terrible. Très, très lent. C'est mauvais et doit être évité.
Grzegorz Grabek
0

SELECT * FROM testcases1 t WHERE NOT EXISTS ( SELECT 1
FROM executions1 i WHERE t.tc_id = i.tc_id and t.pro_id=i.pro_id and pro_id=7 and version_id=5 ) and pro_id=7 ;

Ici, la table testcases1 contient toutes les données et la table executions1 contient des données parmi la table testcases1. Je récupère uniquement les données qui ne sont pas présentes dans la table exections1. (et même je donne quelques conditions à l'intérieur que vous pouvez également donner.) Spécifiez la condition qui ne devrait pas être là dans la récupération des données devrait être entre crochets.

Deepak N
la source
0

cela peut aussi être essayé ...

SELECT l.ip, tbl2.ip as ip2, tbl2.hostname
FROM   login_log l 
LEFT   JOIN (SELECT ip_location.ip, ip_location.hostname
             FROM ip_location
             WHERE ip_location.ip is null)tbl2
Ahnaf
la source
2
WHERE ip_location.ip is null- comment la WHEREcondition peut-elle être jamais vraie? De plus, la sous-requête n'est pas corrélée.
Istiaque Ahmed