JOIN 的 ON 子句中引用的表的顺序是不是重要?
Posted
技术标签:
【中文标题】JOIN 的 ON 子句中引用的表的顺序是不是重要?【英文标题】:Does the order of tables referenced in the ON clause of the JOIN matter?JOIN 的 ON 子句中引用的表的顺序是否重要? 【发布时间】:2010-10-21 14:21:21 【问题描述】:我在 ON 子句中为 JOIN 排序条件的方式是否重要?
select a.Name, b.Status from a
inner join b
on a.StatusID = b.ID
对
select a.Name, b.Status from a
inner join b
on b.ID = a.StatusID
对性能有影响吗?如果我有多个条件怎么办?
一个订单比另一个更易于维护吗?
【问题讨论】:
【参考方案1】:JOIN
可以通过在FROM
子句中将表按正确顺序来强制执行:
mysql 有一个名为 STRAIGHT_JOIN
的特殊子句,它使顺序很重要。
这将使用b.id
上的索引:
SELECT a.Name, b.Status
FROM a
STRAIGHT_JOIN
b
ON b.ID = a.StatusID
这将使用a.StatusID
上的索引:
SELECT a.Name, b.Status
FROM b
STRAIGHT_JOIN
a
ON b.ID = a.StatusID
Oracle 有一个特殊提示 ORDERED
来强制执行 JOIN
顺序:
这将使用b.id
上的索引或在b
上构建哈希表:
SELECT /*+ ORDERED */
*
FROM a
JOIN b
ON b.ID = a.StatusID
这将使用a.StatusID
上的索引或在a
上构建哈希表:
SELECT /*+ ORDERED */
*
FROM b
JOIN a
ON b.ID = a.StatusID
SQL Server 有一个名为 FORCE ORDER
的提示来执行相同的操作:
这将使用b.id
上的索引或在b
上构建哈希表:
SELECT *
FROM a
JOIN b
ON b.ID = a.StatusID
OPTION (FORCE ORDER)
这将使用a.StatusID
上的索引或在a
上构建哈希表:
SELECT *
FROM b
JOIN a
ON b.ID = a.StatusID
OPTION (FORCE ORDER)
PostgreSQL 伙计们,对不起。你的TODO list 说:
优化器提示(不需要)
优化器提示用于解决优化器中的问题。我们宁愿报告和修复问题。
至于比较中的顺序,在任何RDBMS
,AFAIK中都没有关系。
虽然我个人总是尝试估计将搜索哪一列并将该列放在左侧(使其看起来像 lvalue
)。
更多详情请参阅this answer。
【讨论】:
关于“PostgreSQL 伙计们,对不起”-官方 TODO wiki.postgresql.org/wiki/Todo#Features_We_Do_Not_Want 中的“优化器提示”。 我有点惊讶。 OP 询问了ON
子句的顺序,还展示了一个示例,其中他没有反转表,而只反转了 ON
-子句,但这个答案只处理表顺序反转的情况。还是最后一条评论“至于对比中的顺序……”指的是它吗?
@TimSchmelter:至于比较中的顺序,在任何 RDBMS 中都没有关系,AFAIK。【参考方案2】:
不,它没有。
我所做的(为了可读性)是你的第二个例子。
【讨论】:
【参考方案3】:没有。数据库应该根据整个标准确定最佳执行计划,而不是通过按顺序查看每个项目来创建它。您可以通过请求两个查询的执行计划来确认这一点,您会看到它们是相同的(您会发现即使是截然不同的查询,只要它们最终指定相同的逻辑,通常都会编译成相同的执行计划)。
【讨论】:
【参考方案4】:不,没有。归根结底,您实际上只是在评估是否 a=b。
正如平等的对称性所说:
对于任意数量 a 和 b,如果 a = b,则 b = a。所以无论您检查(12)*=12
还是12=(12)*
在逻辑上都没有区别。
如果值相等,则加入,如果不相等,则不加入。无论您是在第一个示例还是第二个示例中指定它,都没有区别。
【讨论】:
这是已发布问题的正确答案。【参考方案5】:正如许多人所说:订单对结果或性能没有影响。
我想指出的是,LINQ to SQL 只允许第一种情况!
例如,下面的例子效果很好,...
var result = from a in db.a
join b in db.b on a.StatusID equals b.ID
select new Name = a.Name, Status = b.Status
...虽然这会在 Visual Studio 中引发错误:
var result = from a in db.a
join b in db.b on b.ID equals a.StatusID
select new Name = a.Name, Status = b.Status
会引发这些编译器错误:
CS1937:名称“name”不在“equals”左侧的范围内。考虑交换 'equals' 两边的表达式。 CS1938:名称“name”不在“equals”右侧的范围内。考虑交换 'equals' 两边的表达式。虽然与标准 SQL 编码无关,但在习惯使用其中任何一种时,这可能是需要考虑的一点。
【讨论】:
【参考方案6】:Read this
SqlServer 包含针对远比这复杂的情况的优化。
如果你有多个标准,通常会懒惰地评估东西(但如果有的话,我需要围绕边缘情况做一些研究。)
为了可读性,我通常更喜欢
SELECT Name, Status FROM a
JOIN b
ON a.StatusID = b.ID
我认为按照声明变量的顺序引用变量更有意义,但这确实是个人品味。
【讨论】:
【参考方案7】:我不使用您的第二个示例的唯一原因:
select a.Name, b.Status
from a
inner join b
on b.ID = a.StatusID
您的用户更有可能回来说“即使他们没有状态记录,我也可以查看所有 a.name 吗?”而不是“即使 b.status 没有名称记录,我也可以看到所有的信息吗?”,所以为了提前计划这个例子,我会使用 On a.StatusID = b.ID
来期待 LEFT Outer Join。这假设您可以有没有“b”的表“a”记录。
更正:它不会改变结果。
这可能是一个有争议的问题,因为用户永远不想改变他们的要求。
【讨论】:
在 LEFT 或 RIGHT 连接中的顺序无关紧要,所以我可以看到在这种情况下并没有真正的区别。即使 ON 子句是“b.ID = a.StatusID”,您仍然可以将子句更改为 LEFT OUTER JOIN @Tom - 很高兴你指出了这一点。我差点惊恐发作。【参考方案8】:不,没关系。但这里有一个示例可以帮助您提高查询的可读性(至少对我而言)
select a.*, b.*
from tableA a
inner join tableB b
on a.name=b.name
and a.type=b.type
每个表引用都在单独的行上,每个连接条件都在单独的行上。制表符有助于保持属于什么的直。
我喜欢做的另一件事是让我在 on 语句中的条件与表格的顺序相同。所以如果先a后b,a在左边,b在右边。
【讨论】:
【参考方案9】:错误:ON 子句在其右侧引用表(php sqlite 3.2)
替换这个
LEFT JOIN itm08 i8 ON i8.id= **cdd01.idcmdds** and i8.itm like '%ormit%'
LEFT JOIN **comodidades cdd01** ON cdd01.id_registro = u.id_registro
为此
LEFT JOIN **comodidades cdd01** ON cdd01.id_registro = u.id_registro
LEFT JOIN itm08 i8 ON i8.id= **cdd01.idcmdds** and i8.itm like '%ormit%'
【讨论】:
以上是关于JOIN 的 ON 子句中引用的表的顺序是不是重要?的主要内容,如果未能解决你的问题,请参考以下文章