使用 LEFT OUTER JOIN 检查相关行不存在的最佳方法是啥

Posted

技术标签:

【中文标题】使用 LEFT OUTER JOIN 检查相关行不存在的最佳方法是啥【英文标题】:What's the best way to use LEFT OUTER JOIN to check for non-existence of related rows使用 LEFT OUTER JOIN 检查相关行不存在的最佳方法是什么 【发布时间】:2011-08-18 16:04:52 【问题描述】:

使用 mysql 5.x 我想有效地选择表 X 中的所有行,其中表 Y 中没有相关行满足某些条件,例如

给我 X 中不存在与 foo = bar 相关的 Y 的所有记录

SELECT count(id) FROM X
LEFT OUTER JOIN Y ON y.X_id = X.id AND y.foo = 'bar'
WHERE y....?

据我了解,左外连接保证为左(第一个)表中的每一行生成一行——在本例中为 X——无论在连接表中是否找到了令人满意的行。然后我想要做的是只选择那些没有找到行的行。

在我看来,如果没有匹配的记录,y.X_id 应该为 NULL,但这个测试似乎不起作用。 y.X_id = 0 或 !y.X_id 也不是。

编辑:更正了多个回复指出的转录错误(ON 而非 AS)。修正语法错误。

【问题讨论】:

【参考方案1】:
SELECT count(id) FROM X 
LEFT OUTER JOIN Y ON (y.X_id = X.id AND y.foo = 'bar')
WHERE y.X_id is null

你很亲密。

首先像往常一样进行连接,然后选择 Y 中的 not null 行实际上是 null 的所有行,因此您可以确定 Y 中存在“不匹配”而不仅仅是 null 值.

还请注意您在查询中所做的错字(已更正):

LEFT OUTER JOIN Y AS
-- should be
LEFT OUTER JOIN Y ON
-- This however is allowed
LEFT OUTER JOIN table2 as Y ON ....

【讨论】:

谢谢。正如我在编辑中提到的,AS 是一个失误。 (当然,实际的查询是一团糟,所以我试图简化。)【参考方案2】:

检查表 Y 的主键是否为 NULL 可以解决问题,这表明连接不匹配:

SELECT count(id) FROM X 
LEFT OUTER JOIN Y ON (y.X_id = X.id AND y.foo = 'bar')
WHERE y.Y_id is null

【讨论】:

@podperson:这是一个非常常见的错误。 SQL 使用的三元逻辑有时甚至会让有经验的 SQL 开发人员感到困惑。 你可以在 MySQL 中做<=> NULL :-)【参考方案3】:

Johan 的回答是 100% 正确的。

除此之外,还有这个选项:

SELECT count(id)
FROM X 
WHERE NOT EXISTS
  ( SELECT * 
    FROM Y
    WHERE (y.X_id = X.id AND y.foo = 'bar')
  )

根据您的表大小和数据分布,这可能更有效。测试并保留两种方式以供将来参考。

【讨论】:

你能详细说明什么时候 NOT EXISTS 更有效吗?也许当表 X 很大而表 Y 很小时?只是好奇,谢谢。 LEFT JOIN / NOT NULL 解决方案和NOT EXISTS 可能会导致执行计划程序的计划不同。这(可能)取决于很多因素,例如您使用的数据库系统(mySQL 或 SQL-Server 或其他)、系统版本、特定引擎(InnoDB 或 MyISAM)、表的大小、分布连接字段的百分比(例如Y 上实际存在的X.id 的百分比),字段中是否存在NULLs,等等。因此,我对这种复杂的依赖关系进行了真正奇妙的演示边距太窄,无法容纳。 你可以阅读这个问题,那里有很多关于这个问题的讨论以及许多指向 SO 之外的材料的链接:***.com/questions/2246772/… 底线是不同的计划将导致(略微或非常)不同的执行时间。在您的机器中使用 您的 数据进行测试,使用各种表大小和设置,使用最适合您的情况。 (当然如果有迹象表明查询很慢。如果它运行得足够快,则无需花时间优化)。 我尝试了 NOT EXISTS(仅在将头撞到 JOIN 解决方案之后),但它在我的脸上爆炸了(我非常小心)。至少根据我的经验,我发现任何涉及子查询的东西都比(小心)JOIN 的性能要低得多。但我正在使用 MySQL5,所以当然是 YMMV。然后这个 Oracle 家伙再次建议将子查询重写为连接以提高性能:dba-oracle.com/oracle_tips_subq_rewrite.htm【参考方案4】:

为什么要使用外连接? 你就不能这样做吗:

SELECT count(id) 
FROM X JOIN Y AS y.X_id = X.id AND y.foo <> 'bar'

【讨论】:

那将选择匹配,OP 正在寻找“不匹配” 它会给他来自 X 的所有行,其中 Y 中有一个匹配的行没有 y.foo = bar (注意 )。我可能误解了这个问题...... An inner join 可以永远给出不匹配的行,它只会隐藏这些行,这就是问题所在。注意外连接是如何对y.X_id = X.id AND y.foo = 'bar' 进行测试的,与之完全相反的是y.X_id &lt;&gt; X.id OR y.foo &lt;&gt; 'bar',但这将导致不同的结果,每个 X 行每个匹配 Y 行。 我确实误解了这个问题。我的错。

以上是关于使用 LEFT OUTER JOIN 检查相关行不存在的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Django 1.9 中表达 sqlite LEFT OUTER JOIN?

sqlite LEFT OUTER JOIN 多个表

关于mysql中的left join和left outer join的区别

MySQL 数据库中 left outer join 和 left join 啥区别

SQL中的left outer join,inner join,right outer join用法详解

SQL Server 中的 LEFT JOIN 与 LEFT OUTER JOIN