在连接表 B 中查找表 A 中没有记录的行

Posted

技术标签:

【中文标题】在连接表 B 中查找表 A 中没有记录的行【英文标题】:Find rows from table A without record in joined table B 【发布时间】:2017-07-05 07:53:29 【问题描述】:

我有两个表,分别称为 Employee(列:Id、Name)和 DataSource(列:Id、EmployeeId、DataSourceName)。

每个员工都可以导出到零个或多个数据源并想象以下情况:

员工表

+----+-------------+
| Id | Name        |
+----+-------------+
| 1  | Ivan        |
| 2  | Adam        |
+----+-------------+

数据源表:

+----+---------------------------------+
| Id | EmplpoyeeId   | DataSourceName  |
+----+---------------------------------+
| 1  | 1             | Source1         |
| 2  | 1             | Source2         |
| 3  | 2             | Source2         |
+----+---------------------------------+

我需要一个查询来确定哪个员工没有被导出到“Source1”(在这种情况下,结果应该是“Adam”,因为他只被导出到了“Source2”)。

表 Employee 和 DataSource 可以有大量记录(数千条)。

有几种技术可以确定它,我们需要找到性能最佳的技术。我想到的很少:

左连接:

SELECT Employee.Id 
FROM Employee 
LEFT JOIN DataSource ON DataSource.EmployeeId = Employee.Id AND DataSource.DataSourceName = 'Source1'
WHERE DataSource.Id IS NULL

内部选择:

SELECT Employee.Id
FROM Employee
WHERE NOT EXIST (SELECT NULL FROM DataSource WHERE DataSource.EmployeeId = Employee.Id AND DataSource.DataSourceName = 'Source1')

例外:

SELECT Employee.ID 
FROM Employee

EXCEPT

SELECT Employee.Id 
FROM Employee 
INNER JOIN DataSource ON DataSource.EmployeeId = Employee.Id AND DataSource.DataSourceName = 'Source1'

在开始对它们进行基准测试之前,我想问一下我是否应该考虑更多方法(并且可能表现良好)。能否请您分享您对最佳性能查询的想法。

【问题讨论】:

您必须只进行测试,但是 WHERE NOT EXISTS “应该”更快,因为它不需要进行完全连接并且可以提前退出......但这将取决于一些因素,所以你真的需要测试 我想是的,我们也可以在找到第一条记录后退出(通过使用 SELECT TOP 1) - 这也可能适用于 WHERE NOT EXISTS 如果在NOT EXIST()中使用select,则不需要使用TOP 1,找到一条记录后会自动停止 我的意思是在顶部查询中使用 TOP 1 而不是在嵌套查询中,因此它声明:SELECT TOP 1 Employee.Id FROM Employee WHERE NOT EXIST ... 【参考方案1】:

如果您需要进一步阅读该主题,这篇文章很好;

http://www.sqlinthewild.co.za/index.php/2010/03/23/left-outer-join-vs-not-exists/

这表明 NOT EXISTS 会表现得更好,因为它不需要完成完全连接(使用反半连接而不是半连接);

“这是这两者之间的主要区别。当使用 LEFT OUTER JOIN ... IS NULL 技术时,SQL 无法判断您只是在检查不存在。优化器还不够聪明(还)。因此它确实完整的连接,然后过滤。NOT EXISTS 过滤器作为连接的一部分。"

【讨论】:

感谢@Milney 我也想知道第三个选项 - 例外,我已将其添加到我的问题中。你觉得这个怎么样? @jabko87 - 这实际上在逻辑上是不同的 - 例如,EXCEPT 将过滤掉重复项(就像您应用了 DISTINCT),因为它是基于集合的操作,如果您使用它,它也会将 NULL 视为值拥有他们。所以你可以使用 except 但必须考虑它是否真的在做同样的事情。我认为它不会更快(因为再次 - 它必须处理整个结果集)所以我会选择 NOT EXISTS .但唯一确定(使用您的特定数据配置文件)的方法是测试!只需运行几次,然后测量差异(使用 SET STATISTICS TIME ON) @jabko87 对性能而言,最重要的是在列(EmployeeId 和 DataSourceName)上有一个索引,并提供最新的统计信息……如果你有,它们的性能都应该可以接受

以上是关于在连接表 B 中查找表 A 中没有记录的行的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL - 如何从连接表中返回单行

MySQL 表连接

连接表:一个是普通表,另一个是 FTS 虚拟表

数据库操作中,左连接,右连接是啥意思,举例说明

mysql左连接右连接(查询两张表不同的数据)

数据结构-线性表自然连接-应用实验