如何从一个表中选择另一张表中不存在的所有记录?
Posted
技术标签:
【中文标题】如何从一个表中选择另一张表中不存在的所有记录?【英文标题】:How to select all records from one table that do not exist in another table? 【发布时间】:2010-04-21 20:18:25 【问题描述】:table1 (id, name) table2 (id, name)
查询:
SELECT name
FROM table2
-- that are not in table1 already
【问题讨论】:
查看底部带有 UNION 的解决方案,它比此处列出的任何其他解决方案都要快几个数量级。 【参考方案1】: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”不会给出正确的结果。我真的不明白为什么,但这是事实,我测试过。【参考方案2】:你可以这样做
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 我们有查询优化器真的那么慢吗?【参考方案3】:我没有足够的代表点来投票 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 秒 答案没有任何特定的顺序,所以第二个答案并不代表你认为的意思。 如果您希望向子查询添加一些额外的过滤器/标准,我认为这也可能是唯一的解决方案。【参考方案4】: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/
【讨论】:
Say NO to Venn Diagrams When Explaining JOINs 太糟糕了,连接图比维恩图更不清晰,更难以直观理解。 感谢您的图表【参考方案5】:这是纯集合论,您可以通过 minus
操作来实现。
select id, name from table1
minus
select id, name from table2
【讨论】:
你觉得这比left join效率高吗? 应该是。减号命令就是针对这种情况而设计的。当然,判断任何特定数据集的唯一方法是两种方式都尝试,看看哪个运行得更快。 在 T-SQL 中,集合运算符是“except”。这对我来说非常方便,并且没有造成任何减速。 在 SQLite 中,“减号”运算符也是“除外”。 MySQL 不支持 MINUS 运算符。【参考方案6】:这是最适合我的方法。
SELECT *
FROM @T1
EXCEPT
SELECT a.*
FROM @T1 a
JOIN @T2 b ON a.ID = b.ID
这是我尝试过的任何其他方法的两倍多。
【讨论】:
谢谢,这也适用于大量数据!但我只是想知道“除外”一词。 在 200k 条记录中的 5k 条记录对我来说是 767 毫秒。其他一切都需要几分钟。【参考方案7】:注意陷阱。如果Table1
中的字段Name
包含Null,您会感到惊讶。
更好的是:
SELECT name
FROM table2
WHERE name NOT IN
(SELECT ISNULL(name ,'')
FROM table1)
【讨论】:
COALESCE > ISNULL(ISNULL 是对语言的无用 T-SQL 补充,没有什么比 COALESCE 更好的新功能)【参考方案8】:您可以在 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/
【讨论】:
【参考方案9】:这对我很有效
SELECT *
FROM [dbo].[table1] t1
LEFT JOIN [dbo].[table2] t2 ON t1.[t1_ID] = t2.[t2_ID]
WHERE t2.[t2_ID] IS NULL
【讨论】:
【参考方案10】:查看查询:
SELECT * FROM Table1 WHERE
id NOT IN (SELECT
e.id
FROM
Table1 e
INNER JOIN
Table2 s ON e.id = s.id);
概念上是:在子查询中获取匹配的记录,然后在主查询中获取不在子查询中的记录。
【讨论】:
【参考方案11】:首先定义表的别名,如t1
和t2
。
之后获得第二张桌子的记录。
之后使用where
条件匹配该记录:
SELECT name FROM table2 as t2
WHERE NOT EXISTS (SELECT * FROM table1 as t1 WHERE t1.name = t2.name)
【讨论】:
你的答案是一样的that。请阅读所有答案,尤其是在回答老问题之前。 别人的专业回答复制!【参考方案12】:上述所有查询在大表上都非常慢。需要改变策略。这是我用于我的数据库的代码,您可以音译更改字段和表名。
策略如下:创建两个隐式临时表并将它们合并。
-
第一个临时表来自第一个原始表的所有行的选择,其中您要控制的字段不存在于第二个原始表中。
第二个隐式临时表包含两个原始表中与您要控制的列/字段的相同值匹配的所有行。
联合的结果是一个表,其中包含多个具有相同控制字段值的行,以防两个原始表(一个来自第一个选择,第二个来自第二个选择),并且只有一行具有控制列值,以防第一个原始表的值与第二个原始表的任何值都不匹配。
你分组和计数。当计数为 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
【讨论】:
【参考方案13】:我将在正确答案中重新发布(因为我还不够酷,无法发表评论)......以防其他人认为需要更好地解释。
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 中它似乎更喜欢空格。
最重要的是,当您使用错误的变量名称时,它会留下问题。我的变量应该更有意义。并且应该有人解释为什么我们需要逗号或不使用逗号。
【讨论】:
【参考方案14】:您可以使用以下查询结构:
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 | |
---|---|---|
1 | 1 | amit@ma.com |
输出:
name |
---|
Sagar |
【讨论】:
【参考方案15】:我尝试了上述所有解决方案,但在我的情况下不起作用。以下查询对我有用。
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 );
【讨论】:
以上是关于如何从一个表中选择另一张表中不存在的所有记录?的主要内容,如果未能解决你的问题,请参考以下文章