【问题标题】:How to select all records from one table that do not exist in another table?如何从一个表中选择另一张表中不存在的所有记录?
【发布时间】:2011-02-10 18:58:23
【问题描述】:

table1 (id, name)
table2 (id, name)

查询:

SELECT name   
FROM table2  
-- that are not in table1 already

【问题讨论】:

  • 查看底部带有 UNION 的解决方案,它比此处列出的任何其他解决方案都要快几个数量级。

标签: sql sql-server tsql


【解决方案1】:

上述所有查询在大表上都非常慢。需要改变策略。这是我用于我的数据库的代码,您可以音译更改字段和表名。

这是策略:创建两个隐式临时表并将它们合并。

  1. 第一个临时表来自第一个原始表的所有行的选择,其中您要控制的字段不存在于第二个原始表中。
  2. 第二个隐式临时表包含两个原始表中与您要控制的列/字段的相同值匹配的所有行。
  3. 联合的结果是一个表,其中包含多个具有相同控制字段值的行,以防两个原始表(一个来自第一个选择,第二个来自第二个选择),并且只有一行具有控制列值,以防第一个原始表的值与第二个原始表的任何值都不匹配。
  4. 你分组和计数。当计数为 1 时,不匹配,最后只选择计数等于 1 的行。

看起来并不优雅,但比上述所有解决方案都要快几个数量级。

重要提示:启用要检查的列上的索引。

SELECT name, source, id
FROM 
(
    SELECT name, "active_ingredients" as source, active_ingredients.id as id 
        FROM active_ingredients

    UNION ALL
        
    SELECT active_ingredients.name as name, "UNII_database" as source, temp_active_ingredients_aliases.id as id 
    FROM active_ingredients
    INNER JOIN temp_active_ingredients_aliases ON temp_active_ingredients_aliases.alias_name = active_ingredients.name

) tbl
GROUP BY name
HAVING count(*) = 1
ORDER BY name

【讨论】:

    【解决方案2】:

    您可以使用以下查询结构:

    SELECT t1.name FROM table1 t1 JOIN table2 t2 ON t2.fk_id != t1.id;

    表 1:

    id name
    1 Amit
    2 Sagar

    表2:

    id fk_id email
    1 1 amit@ma.com

    输出:

    name
    Sagar

    【讨论】:

      【解决方案3】:

      我尝试了上述所有解决方案,但在我的情况下不起作用。以下查询对我有用。

      SELECT name FROM table_1 WHERE name NOT IN (SELECT a.name FROM table_1 AS a 
      LEFT JOIN table_2 as b ON a.name = b.name WHERE ANY FURTHER CONDITION );
      
      

      【讨论】:

        【解决方案4】:

        首先定义表的别名,如t1t2。 之后获得第二张桌子的记录。 之后使用where 条件匹配该记录:

        SELECT name FROM table2 as t2
        WHERE NOT EXISTS (SELECT * FROM table1 as t1 WHERE t1.name = t2.name)
        

        【讨论】:

        • 你的答案是一样的that。请阅读所有答案,尤其是在回答老问题之前。
        • 别人的专业回答复制!
        【解决方案5】:

        我没有足够的代表点来投票 froadie's answer。但我不得不不同意Kris's answer 上的cmets。以下答案:

        SELECT name
        FROM table2
        WHERE name NOT IN
            (SELECT name 
             FROM table1)
        

        FAR 在实践中是否更有效。我不知道为什么,但是我正在针对 800k+ 记录运行它,并且由于上面发布的第二个答案的优势,差异是巨大的。只是我的 0.02 美元。

        【讨论】:

        • 在 NOT IN 查询中,子查询只执行一次,在 EXISTS 查询中,对每一行执行子查询
        • 你太棒了 :) 这样我将使用左连接的 25 秒查询转换为仅 0.1 秒
        • 答案没有任何特定的顺序,所以第二个答案并不代表你认为的意思。
        • 如果您希望向子查询添加一些额外的过滤器/标准,我认为这也可能是唯一的解决方案。
        【解决方案6】:

        查看查询:

        SELECT * FROM Table1 WHERE
        id NOT IN (SELECT 
                e.id
            FROM
                Table1 e
                    INNER JOIN
                Table2 s ON e.id = s.id);
        

        概念上是:在子查询中获取匹配的记录,然后在主查询中获取不在子查询中的记录。

        【讨论】:

          【解决方案7】:
          SELECT <column_list>
          FROM TABLEA a
          LEFTJOIN TABLEB b 
          ON a.Key = b.Key 
          WHERE b.Key IS NULL;
          

          https://www.cloudways.com/blog/how-to-join-two-tables-mysql/

          【讨论】:

          【解决方案8】:

          我将在正确答案中重新发布(因为我还不够酷,无法发表评论)......以防其他人认为需要更好地解释。

          SELECT temp_table_1.name
          FROM original_table_1 temp_table_1
          LEFT JOIN original_table_2 temp_table_2 ON temp_table_2.name = temp_table_1.name
          WHERE temp_table_2.name IS NULL
          

          我已经看到 FROM 中的语法需要在 mySQL 中的表名之间使用逗号,但在 sqlLite 中它似乎更喜欢空格。

          最重要的是,当您使用错误的变量名称时,它会留下问题。我的变量应该更有意义。并且应该有人解释为什么我们需要逗号或不使用逗号。

          【讨论】:

            【解决方案9】:

            你可以这样做

            SELECT name
            FROM table2
            WHERE name NOT IN
                (SELECT name 
                 FROM table1)
            

            SELECT name 
            FROM table2 
            WHERE NOT EXISTS 
                (SELECT * 
                 FROM table1 
                 WHERE table1.name = table2.name)
            

            请参阅this question 了解实现此目的的 3 种技术

            【讨论】:

            • 这对于大量数据来说非常慢。
            • 是的,确实很慢
            • 不存在查询的子查询中不应该是“from table1”吗?
            • 对这如何获得如此多的赞成票感到非常困惑。我发现很难想出使用它的理由,因为有一种方法可以在几乎相同的击键次数下更快地解决这个问题。
            • @searchengine27 我们有查询优化器真的那么慢吗?
            【解决方案10】:

            这是最适合我的方法。

            SELECT *
            FROM @T1
            EXCEPT
            SELECT a.*
            FROM @T1 a
            JOIN @T2 b ON a.ID = b.ID
            

            这是我尝试过的任何其他方法的两倍多。

            【讨论】:

            • 谢谢,这也适用于大量数据!但我只是想知道“除外”一词。
            【解决方案11】:

            这对我很有效

            SELECT * 
            FROM [dbo].[table1] t1
            LEFT JOIN [dbo].[table2] t2 ON t1.[t1_ID] = t2.[t2_ID]
            WHERE t2.[t2_ID] IS NULL
            

            【讨论】:

              【解决方案12】:
              SELECT t1.name
              FROM table1 t1
              LEFT JOIN table2 t2 ON t2.name = t1.name
              WHERE t2.name IS NULL
              

              :这里发生了什么?

              A:从概念上讲,我们从table1 中选择所有行,并且对于每一行,我们尝试在table2 中找到与name 列具有相同值的行。如果没有这样的行,我们只需将该行的结果的table2 部分留空。然后我们通过仅选择结果中不存在匹配行的那些行来限制我们的选择。最后,我们忽略结果中的所有字段,除了name 列(我们确定存在的字段,来自table1)。

              虽然它可能不是在所有情况下都可能是性能最高的方法,但它应该适用于几乎所有尝试实现 ANSI 92 SQL 的数据库引擎

              【讨论】:

              • @z-boss:它也是 SQL Server 上性能最低的:explainextended.com/2009/09/15/…
              • @BunkerBoy:左连接允许右侧的行不存在,而不会影响左侧行的包含。内连接需要左右两边的行都存在。我在这里做的是应用一些逻辑来基本上获得内部连接的反向选择。
              • 天哪,这有助于非常轻松地进行可视化,其他人将其视为 5 种不同的方式,但这有所帮助。简单:首先你得到左连接,A 中的所有内容,以及 B 中与 A 匹配的所有内容。但是就像在不连接的左连接字段中发生的那样,它只是空的。然后你告诉我,好吧,我只希望那是空的。这样,您现在在 A 中拥有所有在 B 中没有匹配的行
              • 应该指出,这个解决方案(被接受并投票赞成)是唯一的,我认为,可以针对多个字段发挥作用的场景进行编辑。具体来说,我从表一中返回字段、字段 2、字段 3,其中字段 ad field2 的组合不在第二个表中。除了修改这个答案中的连接之外,我看不到用下面争论的其他一些“更有效的答案”来做到这一点
              • 只要确保使用“WHERE t2.name IS NULL”而不是“AND t2.name IS NULL”,因为“and”不会给出正确的结果。我真的不明白为什么,但这是事实,我测试过。
              【解决方案13】:

              您可以在 mssql 中使用 EXCEPT 或在 oracle 中使用 MINUS,它们是相同的:

              http://blog.sqlauthority.com/2008/08/07/sql-server-except-clause-in-sql-server-is-similar-to-minus-clause-in-oracle/

              【讨论】:

                【解决方案14】:

                注意陷阱。如果Table1 中的字段Name 包含Null,您会感到惊讶。 更好的是:

                SELECT name
                FROM table2
                WHERE name NOT IN
                    (SELECT ISNULL(name ,'')
                     FROM table1)
                

                【讨论】:

                • COALESCE > ISNULL(ISNULL 是对语言的无用 T-SQL 补充,没有什么比 COALESCE 更好的新功能)
                【解决方案15】:

                这是纯集合论,您可以通过 minus 操作来实现。

                select id, name from table1
                minus
                select id, name from table2
                

                【讨论】:

                • 你觉得这比left join效率高吗?
                • 应该是。减号命令就是针对这种情况而设计的。当然,判断任何特定数据集的唯一方法是两种方式都尝试,看看哪个运行得更快。
                • 在 T-SQL 中,集合运算符是“except”。这对我来说非常方便,并且没有造成任何减速。
                • 在 SQLite 中,“减号”运算符也是“除外”。
                • MySQL 不支持 MINUS 运算符。
                猜你喜欢
                • 2020-06-10
                • 2022-11-13
                • 1970-01-01
                • 2016-02-29
                • 1970-01-01
                • 2015-08-27
                相关资源
                最近更新 更多