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


469

table1(标识,名称)
table2(标识,名称)

查询:

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

Answers:


843
SELECT t1.name
FROM table1 t1
LEFT JOIN table2 t2 ON t2.name = t1.name
WHERE t2.name IS NULL

问:这是怎么回事?

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

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


16
@ Z-老板:这也是SQL Server上的至少高性能:explainextended.com/2009/09/15/...
— OMG小马

7
@BunkerBoy:左联接允许不存在右侧行,而不会影响左侧行的包含。内部联接要求左右两行都存在。我在这里所做的是应用一些逻辑来基本上得到内部联接的反向选择。
— 克里斯(Kris)

2
omg这有助于非常容易地将其可视化,其他人则用5种不同的方式表示它,但这有所帮助。很简单:首先您要获得左联接,A中的所有内容以及B中与A匹配的所有内容。但是,在不联接的左联接字段中,情况恰好为空。然后您说,好吧,我只希望它们为null。这样,您现在拥有A中没有匹配项的所有行B
— 穆罕默德·乌默尔

7
应当指出的是,我认为,只有针对一个以上领域起作用的情况,才能对这一解决方案(接受并投票赞成)进行编辑。具体来说,我要从表1返回字段2,字段3,其中字段ad field2的组合不在第二个表中。除了修改此答案中的
— 联接外

1
只要确保使用“ WHERE t2.name IS NULL”而不是“ AND t2.name IS NULL”,因为“ and”将无法给出正确的结果。我真的不明白为什么,但这是事实,我对此进行了测试。
— user890332 '19

236

你可以做

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)

查看此问题的3种技巧来完成此任务


38
对于大量数据,这非常慢。
— Lightbulb1

是的,确实的确很慢
— sirus

它不应该是不存在查询的子查询中的“ from table1”。
— 猎犬

对于如何获得如此多的投票感到非常困惑。我发现很难想到有一个理由使用这种方法,因为有一种方法可以用几乎相同的击键次数来更快地解决这个问题。
— searchengine27

这对我
— 有用

81

我的代表点不足,无法投票赞成第二个答案。但是我必须不同意关于最佳答案的评论。第二个答案:

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

FAR在实践中更有效吗?我不知道为什么,但是我将其运行在800,000条以上的记录上,并且上述第二个答案的优势非常明显。我的$ 0.02


30
在NOT IN查询中,子查询仅执行一次,而在EXISTS查询中,每行执行一次子查询
— Carrick 2014年

1
您很棒:)这样,我将使用左
— 联接的

3
答案没有特定顺序,因此第二个答案并不代表您的意思。

38

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

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

您认为这比左联接有效吗?
— uhs

它应该是。minus命令是针对这种情况而设计的。当然,判断任何特定数据集的唯一方法是尝试两种方法并查看运行速度更快。
— 2014年

9
在T-SQL中,集合运算符为“ except”。这对我来说非常方便,并且没有导致任何速度下降。

2
在SQLite中,“减号”运算符也为“除外”。
— lifjoy

MySQL不支持MINUS运算符。
— Muhammad Azeem


16

当心陷阱。如果该字段Name中Table1包含空值你是在惊喜。更好的是:

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

1
COALESCE> ISNULL(ISNULL是对该语言的无用的T-SQL补充,没有比COALESCE有用的新功能或更好的功能)
— Kris

14

这是最适合我的东西。

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

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


谢谢,这也可以处理大量数据!但是我只是想知道“例外”这个词。
— PatsonLeaner



1

查看查询:

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

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


0

我将以正确答案重新发布(因为我还没有足够的评价能力),以防其他人认为需要更好的解释。

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中,它似乎更喜欢空格。

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


0

如果要选择特定用户

SELECT tent_nmr FROM Statio_Tentative_Mstr
WHERE tent_npk = '90009'
AND
tent_nmr NOT IN (SELECT permintaan_tent FROM Statio_Permintaan_Mstr)

的tent_npk是用户的主键

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.