web-dev-qa-db-fra.com

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

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 de login_log qui n'a pas de ligne dans ip_location.
J'ai essayé cette requête, mais une erreur de syntaxe est émise.

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 aussi si cette requête (avec les ajustements nécessaires) est la requête la plus performante à cet effet.

118
stUrb

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

NOT EXISTS

Souvent le plus rapide dans 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. Il en résulte 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 si EXCEPT ALL est utilisé.

En règle générale, vous souhaiterez le mot clé ALL. Si vous ne vous en souciez pas, utilisez-le quand même car cela rend la requête plus rapide.

NOT IN

Seulement bon sans les valeurs NULL ou si vous savez manipuler NULL correctement. Je voudrais pas l'utiliser à cette fin. 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 IN comporte un "piège" pour les valeurs NULL de chaque côté:

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

287
Erwin Brandstetter

A.) La commande est NOT EXISTS, 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
  )
;
3
caleb.breckon

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
0
Ahnaf

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 quelques données parmi la table testcases1. Je ne récupère que les données qui ne sont pas présentes dans la table exections1. (et même je donne certaines conditions à l'intérieur que vous pouvez aussi donner.) spécifiez une condition qui ne devrait pas être là pour récupérer des données devrait être entre crochets. 

0
Deepak N