Seleccionar filas que no están presentes en otra tabla


Tengo dos tablas de postgresql:

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

Quiero obtener todas las direcciones IP de login_log que no tienen una fila en ip_location.
He intentado esta consulta, pero arroja un error de sintaxis.

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`

También me pregunto si esta consulta (con ajustes para que funcione) es la consulta que mejor funciona para este propósito.

Author: Radek Postołowicz, 2013-10-14

4 answers

Hay básicamente 4 técnicas para esta tarea, todas ellas SQL estándar.

NOT EXISTS

A menudo, esto es más rápido en Postgres.

SELECT ip 
FROM   login_log l 
WHERE  NOT EXISTS (
   SELECT -- mostly irrelevant what's here; might just be empty in pg
   FROM   ip_location i
   WHERE  l.ip = i.ip
   );

También considere:

LEFT JOIN / IS NULL

A veces esto es más rápido. A menudo más corto.

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

Corto. No tan fácilmente integrado en más complejo consulta.

SELECT ip 
FROM   login_log

EXCEPT ALL               -- ALL, to keep duplicate rows and make it faster
SELECT ip
FROM   ip_location;

Tenga en cuenta que ( por documentación):

Los duplicados se eliminan a menos que se utilice EXCEPT ALL.

Normalmente, querrás la palabra clave ALL. Si no te importa, sigue usándolo porque hace que la consulta sea más rápida.

NOT IN

Solo es bueno sin valores NULL o si sabes manejar NULL correctamente! Yo no lo usaría para este propósito. El rendimiento puede deteriorarse con más grande tabla.

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

NOT IN lleva una" trampa " para NULL valores a cada lado:

Pregunta similar sobre dba.SE dirigido a MySQL:

 259
Author: Erwin Brandstetter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-10-05 17:22:35

A.) El comando NO EXISTE, te falta la 'S'.

B.) NO usar en su lugar

SELECT ip 
  FROM login_log 
  WHERE ip NOT IN (
    SELECT ip
    FROM ip_location
  )
;
 3
Author: caleb.breckon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-14 15:30:55

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 ;

Aquí la tabla testcases1 contiene todos los datos y executions1 la tabla contiene algunos datos entre la tabla testcases1. Estoy recuperando solo los datos que no están presentes en la tabla exections1. (e incluso yo estoy dando algunas condiciones en el interior que usted también puede dar.) especificar la condición que no debe estar allí en la recuperación de datos debe estar dentro de corchetes.

 0
Author: Deepak N,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-04 08:29:25

Esto también se puede intentar...

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
Author: Ahnaf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-19 00:03:44