Выберите строки, которых нет в другой таблице

У меня есть две таблицы postgresql:

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

Я хочу получить каждый IP-адрес от login_log который не имеет строки в ip_location,
Я пробовал этот запрос, но он выдает синтаксическую ошибку.

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`

Мне также интересно, является ли этот запрос (с изменениями, чтобы он работал) наиболее эффективным для этой цели.

4 ответа

Решение

Есть в основном 4 метода для этой задачи, все они стандартные SQL.

NOT EXISTS

Часто самый быстрый в 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
   WHERE  ip = l.ip
   );

Также учтите:

LEFT JOIN / IS NULL

Иногда это быстрее всего. Часто самый короткий.

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

Короткий. Не так легко интегрировать в более сложные запросы.

SELECT ip 
FROM   login_log

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

Обратите внимание, что ( согласно документации):

дубликаты исключены, если EXCEPT ALL используется.

Как правило, вы хотите ALL ключевое слово. Если вам все равно, все равно используйте его, потому что это делает запрос быстрее.

NOT IN

Только хорошо без NULL значения или если вы знаете, чтобы справиться NULL должным образом. Я бы не использовал это для этой цели. Производительность может ухудшиться с большими таблицами.

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

NOT IN несет "ловушку" для NULL значения по обе стороны:

Аналогичный вопрос на dba.SE, ориентированный на MySQL:

A.) Команда НЕ СУЩЕСТВУЕТ, вам не хватает "S".

B.) Вместо этого используйте NOT IN

SELECT ip 
  FROM login_log 
  WHERE ip NOT IN (
    SELECT ip
    FROM ip_location
  )
;

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 ;

Здесь таблица testcases1 содержит все данные, а таблица выполнения - содержит некоторые данные из таблицы testcases1. Я получаю только данные, которых нет в таблице exections1. (и даже я даю некоторые условия внутри, которые вы также можете указать.) Укажите условие, которого не должно быть при извлечении данных, должно быть в скобках.

Это тоже можно попробовать...

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
Другие вопросы по тегам