优化器可能会受到 a.cat、b.cat 或 c.cat 中是否存在可用索引以及该索引是否也包括相关的 id 或 fid 列的影响。将简单的 WHERE 子句谓词下推到表级操作可能足够聪明。它也可能受到表的统计信息的影响。(如果参数的值恰好在 C 中出现一次,则开始处理 C 可能比开始处理 A 更有效;或者从 B 开始可能更有效;或者仍然可能更有效从 A 开始。优化器在执行语句之前不知道参数的值,而不是在准备好时能够看到它,这可能会也可能无关紧要。)
因此,正如评论中已经暗示的那样,如果不了解您正在使用的系统的更多信息(包括模式等),就无法做出明确的声明。
好消息是结果内容应该是一样的——不能保证执行计划。
我注意到大多数 SQL 系统需要一个关键字 ON 用于每个连接:
select *
from A as a
join B as b
on b.id = a.id
AND b.cat = @cat
join C as c
on c.fid = b.fid
AND c.cat = @cat
where a.cat = @cat
赛瑞恩问道:
鉴于执行计划可能不同,哪种查询方式“更好”。哪里更好被定义为:
- 更有可能被优化以提供更好的计划(或者不太可能混淆 SQL 优化器)。
- 更符合 SQL 约定。
这在一定程度上取决于 DBMS 及其优化器;在这方面没有经验主义的替代品,但请记住,在某个时间点凭经验确定的内容在另一个时间点可能是错误的,因为:
- 数据集的大小发生了变化
- 数据集现在有不同的统计分布
- 优化器可能已更改
- 可能有不同的索引可用
有很多方法可以进行连接,它(仍然)依赖于可用的索引。我可能会写:
SELECT *
FROM A JOIN B ON b.id = a.id AND b.cat = a.cat
JOIN C ON c.fid = b.fid AND c.cat = b.cat
WHERE a.cat = @cat
AND b.cat = @cat
AND c.cat = @cat
这里的逻辑是,A(id, cat) 和 B(id, cat) 上,B(fid, cat) 和 C(fid, cat) 上可能都有索引,优化器因此可以充分利用那些索引。WHERE 子句包含两个冗余项,但让优化器知道需要什么,并明确告诉它可能无法自行推断出的内容。如果您对优化器的质量有信心(并检查了它生成的查询计划),那么您可以消除 WHERE 子句中三个条件中的两个。
如果将参数放在 ON 子句中,优化器可能无法像没有它那样充分利用索引 - 同样,您必须尝试找出优化器的行为方式。
最后,正如已经暗示的那样,表上的索引集至关重要,确保 DBMS 需要的任何统计信息都是最新的通常很重要。还要记住,如果表最初很小,优化器可能会选择与表变大时不同的查询计划——所以在合理的水平上进行测试。除非您的生产表只包含几十行,否则不要对只有几十行的表的查询进行性能测试。