选择其他表中不存在的行


172

我有两个postgresql表:

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

我想获取login_log没有行的每个IP地址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`

我也想知道此查询(进行调整以使其工作)是否是为此目的性能最佳的查询。

Answers:


386

基本上有4种技术可以完成此任务,所有技术都是标准SQL。

NOT EXISTS

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

同时考虑:

LEFT JOIN / IS NULL

有时这是最快的。通常最短。通常会产生与相同的查询计划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

短。不太容易集成到更复杂的查询中。

SELECT ip 
FROM   login_log

EXCEPT ALL  -- "ALL" keeps duplicates and makes 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 INNULL在任一侧带有一个“陷阱”以表示值:

针对MySQL的dba.SE的类似问题:


2
考虑到两个表中的数据量都很大,哪种SQL将运行得更快。(假设为十亿)
Teja

EXCEPT ALL是最快的,我
丹帕克

请注意LEFT JOIN-如果查找表中有多个匹配的行,这将在主查询中为每个匹配的行创建一个重复的条目,这可能是不需要的。
Matthias Fripp

@MatthiasFripp:除非使用永远不会发生WHERE i.ip IS NULL否则根本没有匹配。
Erwin Brandstetter

@ erwin-brandstetter:好点。我考虑了多次正面比赛的可能性而使自己绊倒了,但当然这些都将被排除在外。
Matthias Fripp

2

A.)该命令不存在,您缺少“ S”。

B.)改用NOT IN

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

4
在大型数据集上禁止输入是一个可怕的想法。非常非常慢 这是不好的,应该避免。
Grzegorz Grabek '18年

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 ;

这里testcases1表包含所有数据,executions1表包含testcases1表中的一些数据。我只检索exections1表中不存在的数据。(甚至我在里面给出了一些您也可以给出的条件。)指定在检索数据时不应出现的条件应放在方括号内。


0

这也可以尝试...

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

2
WHERE ip_location.ip is null- WHERE条件怎么可能是真的?而且,子查询也不是相关查询。
Istiaque Ahmed
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.