更详细地解释 JOIN 与 LEFT JOIN 和 WHERE 条件性能建议
Posted
技术标签:
【中文标题】更详细地解释 JOIN 与 LEFT JOIN 和 WHERE 条件性能建议【英文标题】:Explain JOIN vs. LEFT JOIN and WHERE condition performance suggestion in more detail 【发布时间】:2014-09-12 15:24:00 【问题描述】:在this candidate answer 中断言JOIN
在某些涉及一些WHERE
子句的情况下优于LEFT JOIN
,因为它不会混淆查询计划器并且不是“毫无意义的”。断言/假设是对任何人都应该是显而易见的。
请进一步解释或提供链接以供进一步阅读。
【问题讨论】:
更好?它们用于不同的目的。 JOIN 是内连接,LEFT JOIN 是外连接(和 LEFT OUTER JOIN 一样)。根据您的意图,您将使用外部联接或内部联接。 WHERE 子句不应用于连接条件。它应该用于其他标准; IE。过滤。 我在那里读到的答案是“由于 LEFT JOIN(带有 WHERE)实际上是一个 INNER JOIN,所以只需使用一个 INNER JOIN。”我不确定它对 [特定] 查询计划器有多“混乱”,但它对于人类 [阅读:我的] 消费来说并不理想,IMOHO。 (例如,当 INNER 就足够时,使用 CROSS JOIN 也是如此。) 带有使用外连接表的 where 子句的左连接实际上是内连接。但是,如果 where 子句不使用外连接表,不,情况并非如此。 【参考方案1】:实际上,WHERE
条件和JOIN
条件在 PostgreSQL 中是 100% 等效的。 (不过,最好使用显式的 JOIN
条件使查询更易于阅读和维护)。
不同样适用于 LEFT JOIN
与连接右侧表上的 WHERE
条件相结合。 LEFT JOIN
的目的是保留连接左侧的所有行,而不考虑右侧的匹配项。如果未找到匹配项,则使用右侧列的 NULL
值扩展该行。 The manual:
LEFT OUTER JOIN
首先,执行内部连接。然后,对于 T1 中与 T2 中的任何行不满足连接条件的每一行,一个连接行 在 T2 的列中添加空值。因此,连接表 T1 中的每一行总是至少有一行。
如果您随后在右侧表格的列上应用WHERE
条件,该条件需要除NULL
值以外的其他值,则您会取消效果并强制转换LEFT [OUTER] JOIN
以像普通[INNER] JOIN
一样工作,只是(可能)由于更复杂的查询计划而更昂贵。
在具有许多连接表的查询中,Postgres(或任何 RDBMS)很难找到最佳(甚至是好的)查询计划。连接表的理论上可能的序列数量成因地 (!) 增加。 Postgres 使用"Generic Query Optimizer" 来执行任务,并且有一些设置会影响它。
如前所述,使用误导性的 LEFT JOIN
混淆查询,会使查询规划器的工作更加困难,会误导人类读者,并且通常会提示查询逻辑中的错误。
由此产生的问题的相关答案:
Why does null equal integer in WHERE? Query with LEFT JOIN not returning rows for count of 0 SQL query using outer join and limiting child records for each parent Left outer join acting like inner join Select rows which are not present in other table等等
【讨论】:
如果真的“更贵”?现代查询规划器——当然,我主要使用 SQL Server——给我留下了深刻的印象。 贵不贵有什么不同。如果您要说表 X 上的值应该是某个值(不是空值),为什么还要对表 X 使用外连接?如果有的话,不要仅仅因为它没有意义就这样做。 @user2864740:在简单的情况下这并不重要。但是,它使查询优化器在复杂情况下的工作变得更加困难。但正如布赖恩所评论的那样:无论哪种方式都不要这样做。对于人类读者来说,这也很令人困惑。 @ErwinBrandstetter,感谢您提供了一个非常好的答案,很抱歉之前的混淆,Brian 的例子让事情变得非常清楚,所以我接受了他的回答。 @DwayneTowell:我添加了一些例子来说明。【参考方案2】:考虑以下示例。我们有两张表,DEPARTMENTS 和 EMPLOYEES。
有些部门还没有员工。
此查询使用内部联接来查找部门员工 999 工作的部门(如果有),否则它不显示任何内容(甚至不显示员工或其姓名):
select a.department_id, a.department_desc, b.employee_id, b.employee_name
from departments a
join employees b
on a.department_id = b.department_id
where b.employee_id = '999'
下一个查询使用外部联接(部门和员工之间的左侧)并查找员工 999 工作的部门。但是,如果员工不在任何部门工作,它也不会显示员工的 ID 或他或她的姓名。这是因为在 WHERE 子句中使用了外部连接表。如果没有匹配的部门,则为空(不是 999,即使员工中存在 999)。
select a.department_id, a.department_desc, b.employee_id, b.employee_name
from departments a
left join employees b
on a.department_id = b.department_id
where b.employee_id = '999'
但是考虑一下这个查询:
select a.department_id, a.department_desc, b.employee_id, b.employee_name
from departments a
left join employees b
on a.department_id = b.department_id
and b.employee_id= '999'
现在条件在 on 子句中。所以即使这个员工没有在任何部门工作,他仍然会被退回(他的 ID 和姓名)。部门列将为空,但我们得到一个结果(员工方面)。
您可能认为您永远不想在 WHERE 子句中使用外连接表,但事实并非如此。但通常情况下,出于上述原因。
假设您希望所有部门都没有员工。然后你可以运行下面的语句,它确实使用了外连接,外连接表用在 where 子句中:
select a.department_id, a.department_desc, b.employee_id
from departments a
left join employees b
on a.department_id = b.department_id
where b.employee_id is null
^^ 显示没有员工的部门。
以上可能是您希望在 WHERE 子句而不是 ON 子句中使用外部联接表的唯一正当理由(我认为这是您的问题;内部联接和外部联接之间的区别是完全不同的主题)。
查看的一个好方法是:您使用外连接来允许空值。为什么你会使用外连接并说一个字段不应该为空并且应该等于'XYZ'?如果一个值必须是“XYZ”(不是空值),那么为什么要指示数据库允许空值返回呢?这就像说一件事,然后再覆盖它。
【讨论】:
这个答案中的第三个例子是错误的。在这种情况下,不会返回员工,该查询将返回 所有部门 和 ID 为 999 的任何关联员工。如果 999 员工未与任何部门关联,则不会在结果中返回。 看起来像“但考虑这个查询”上面和下面的查询是一样的。 区别在于 WHERE 与 AND(留在 LEFT JOIN "ON" 的一部分)以上是关于更详细地解释 JOIN 与 LEFT JOIN 和 WHERE 条件性能建议的主要内容,如果未能解决你的问题,请参考以下文章
最全解释Mysql 的join中on与where 过滤条件差异
sql语法:inner join on, left join on, right join on详细使用方法
sql语法:inner join on, left join on, right join on详细使用方法
超详细mysql left join,right join,inner join用法分析