【问题标题】:Select rows which are not present in other table选择其他表中不存在的行
【发布时间】:2013-10-22 05:02:41
【问题描述】:

我有两个 postgresql 表:

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

我想从login_log 获取在ip_location 中没有一行的每个IP 地址。
我尝试了这个查询,但它引发了语法错误。

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`

我还想知道这个查询(经过调整以使其工作)是否是为此目的表现最好的查询。

【问题讨论】:

    标签: sql postgresql null left-join exists


    【解决方案1】:

    这个任务基本上有 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;
    

    请注意 (per documentation):

    除非使用EXCEPT ALL,否则会消除重复。

    通常,您需要ALL 关键字。如果您不在乎,请继续使用它,因为它使查询更快

    NOT IN

    只有在没有 NULL 值或知道如何正确处理 NULL 时才有效。 I would not use it for this purpose. 此外,更大的表可能会降低性能。

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

    NOT IN 在任一侧为NULL 值携带一个“陷阱”:

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

    【讨论】:

    • 考虑到两个表中的数据量都很大,哪个 SQL 会运行得更快。 (假设以十亿为单位)
    • EXCEPT ALL 对我来说是最快的
    • 小心LEFT JOIN - 如果查找表中有多个匹配行,这将在您的主查询中为每个匹配行创建一个重复条目,这可能是不需要的。
    • @MatthiasFripp:除了 WHERE i.ip IS NULL 永远不会发生这种情况,这意味着 no 完全匹配。
    • @erwin-brandstetter:说得好。考虑到多个正匹配的可能性,我把自己绊倒了,但当然这些都会被排除在外。
    【解决方案2】:

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

    B.) 改用 NOT IN

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

    【讨论】:

    • 不在大型数据集上是一个糟糕的主意。非常非常慢。这是不好的,应该避免。
    • @GrzegorzGrabek 尝试提供替代方案,而不是仅仅忽略其他人的答案
    • @TheRealChx101 评论是根据 Celeb 的建议写的,使用 NOT IN 而不是 NOT EXISTS。它(评论)对于大多数人来说是非常清楚的,因为你可以看到有多少人投票给这条评论有用。
    【解决方案3】:

    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 表中的一些数据。我只检索 executions1 表中不存在的数据。 (甚至我在里面给出了一些你也可以给出的条件。)指定在检索数据时不应该存在的条件应该在括号内。

    【讨论】:

      【解决方案4】:

      这个也可以试试……

      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
      

      【讨论】:

      • WHERE ip_location.ip is null - WHEREcondition 怎么可能是真的?此外,子查询不是相关的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-08
      • 1970-01-01
      • 2020-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多