如何从一个表中选择另一个表中不存在的所有记录?

z-b*_*oss 423 sql t-sql sql-server

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

查询:

SELECT name   
FROM table2  
-- that are not in table1 already
Run Code Online (Sandbox Code Playgroud)

Kri*_*ris 776

SELECT t1.name
FROM table1 t1
LEFT JOIN table2 t2 ON t2.name = t1.name
WHERE t2.name IS NULL
Run Code Online (Sandbox Code Playgroud)

:这里发生了什么?

:从概念上讲,我们选择所有行table1和我们尝试table2使用相同值name列的行来查找每一行.如果没有这样的行,我们只是将table2结果的部分留空.然后我们通过仅选择匹配行不存在的结果中的那些行来约束我们的选择.最后,我们忽略了结果中的所有字段,除了name列(我们确定存在的列,来自table1).

尽管在所有情况下它可能不是最高性能的方法,但它应该在基本上每个尝试实现ANSI 92 SQL的数据库引擎中工作.

  • @ z-boss:它也是SQL Server上性能最差的:http://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left-join-is-null-sql-服务器/ (16认同)
  • 应该注意的是,这个解决方案(被接受和投票)是唯一一个,我认为,可以编辑一个场景,其中有多个场发挥作用.具体来说,我从表1返回字段,字段2,字段3,其中字段ad field2的组合不在第二个表中.除了在这个答案中修改联接之外,我没有看到一种方法来解决下面提到的其他一些"更有效的答案" (7认同)
  • @BunkerBoy:左连接允许右侧的行不存在而不会影响左侧的行包含.内部联接需要左侧和右侧的行存在.我在这里做的是应用一些逻辑来基本上得到内连接的反向选择. (5认同)
  • omg这有助于非常容易地将其可视化,其他人则用5种不同的方式表示它,但这有所帮助。很简单:首先您要获得左连接,A中的所有内容以及B中与A匹配的所有内容。但是,在不连接的左连接字段中,情况恰好为空。然后您说,好吧,我只希望它们为null。这样,您现在拥有A中所有不匹配的行。 (2认同)
  • 只需确保您使用“WHERE t2.name IS NULL”而不是“AND t2.name IS NULL”,因为“and”不会给出正确的结果。我不太明白为什么,但这是事实,我测试过。 (2认同)

fro*_*die 219

你可以这样做

SELECT name
FROM table2
WHERE name NOT IN
    (SELECT name 
     FROM table1)
Run Code Online (Sandbox Code Playgroud)

要么

SELECT name 
FROM table2 
WHERE NOT EXISTS 
    (SELECT * 
     FROM table1 
     WHERE table1.name = table2.name)
Run Code Online (Sandbox Code Playgroud)

有关实现此目的的3种技术,请参阅此问题

  • 大量数据的速度非常慢. (34认同)
  • "not in"语法也适用于SQLite. (5认同)

Tan*_*aei 71

我没有足够的代表来推荐第二个答案.但我不同意对最佳答案的评论.第二个答案:

SELECT name
FROM table2
WHERE name NOT IN
    (SELECT name 
     FROM table1)
Run Code Online (Sandbox Code Playgroud)

FAR在实践中是否更有效率.我不知道为什么,但是我在800k +记录中运行它,差异很大,因为上面给出的第二个答案给出了优势.只需我0.02美元

  • 在NOT IN查询中,子查询仅执行一次,在EXISTS查询中,对每一行执行子查询 (22认同)
  • 你太棒了 :) 通过这种方式,我将使用左连接的 25 秒查询转换为 0.1 秒 (4认同)
  • 答案没有特定顺序,因此*第二个答案*并不意味着您认为的意思。 (2认同)

Win*_*ter 37

这是您可以通过minus操作实现的纯粹集合理论.

select id, name from table1
minus
select id, name from table2
Run Code Online (Sandbox Code Playgroud)

  • 在T-SQL中,set运算符是"except".这对我来说非常方便,并没有造成任何减速. (9认同)
  • 在 SQLite 中,“减号”运算符也是“除外”。 (4认同)
  • 它应该是。减号命令专为这种情况而设计。当然,判断任何特定数据集的唯一方法是同时尝试两种方式,看看哪个运行得更快。 (3认同)
  • MySQL 不支持 MINUS 运算符。 (2认同)

Anu*_*raj 25

SELECT <column_list>
FROM TABLEA a
LEFTJOIN TABLEB b 
ON a.Key = b.Key 
WHERE b.Key IS NULL;
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

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

  • [解释 JOIN 时对维恩图说不](https://blog.jooq.org/2016/07/05/say-no-to-venn-diagrams-when-explaining-joins/) (12认同)
  • 不幸的是,连接图比维恩图不太清晰,也更难直观地理解。 (8认同)
  • 谢谢你的图表 (2认同)

小智 15

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

SELECT name
FROM table2
WHERE name NOT IN
    (SELECT ISNULL(name ,'')
     FROM table1)
Run Code Online (Sandbox Code Playgroud)

  • COALESCE &gt; ISNULL(ISNULL 是对语言的无用 T-SQL 补充,与 COALESCE 相比没有任何新功能或更好) (2认同)

小智 13

这对我来说最有效.

SELECT *
FROM @T1
EXCEPT
SELECT a.*
FROM @T1 a
JOIN @T2 b ON a.ID = b.ID
Run Code Online (Sandbox Code Playgroud)

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


Dav*_*wzy 7

那项工作对我来说很敏感

SELECT * 
FROM [dbo].[table1] t1
LEFT JOIN [dbo].[table2] t2 ON t1.[t1_ID] = t2.[t2_ID]
WHERE t2.[t2_ID] IS NULL
Run Code Online (Sandbox Code Playgroud)


fed*_*ari 5

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

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

  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
Run Code Online (Sandbox Code Playgroud)


w.D*_*aya 5

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

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

表格1 :

ID 姓名
1 阿米特
2 萨加尔

表2:

ID 外键ID 电子邮件
1 1 amit@ma.com

输出:

姓名
萨加尔