## 14.3.用显式方法控制计划员`参加`条款 [](<>) 通过使用显式`参加`语法。要了解这一点的重要性,我们首先需要一些背景知识。 在简单的联接查询中,例如: ``` SELECT * FROM a, b, c WHERE a.id = b.id AND b.ref = c.id; ``` 计划者可以按任意顺序加入给定的表。例如,它可以使用`哪里`条件`a、 id=b.id`,然后使用另一个`哪里`条件或者它可以将B连接到C,然后将A连接到该结果。或者它可以把A和C连接起来,然后把它们和B连接起来——但这将是低效的,因为A和C的完全笛卡尔积必须形成,而在这个过程中没有适用的条件`哪里`子句以允许优化联接。(PostgreSQL executor中的所有连接都发生在两个输入表之间,因此有必要以其中一种方式建立结果。)重要的一点是,这些不同的连接可能性给出了语义上等价的结果,但可能会有巨大不同的执行成本。因此,计划者将对所有这些问题进行研究,试图找到最有效的查询计划。 当一个查询只涉及两个或三个表时,不需要担心太多的联接顺序。但是,随着表的数量增加,可能的连接顺序的数量呈指数增长。除了十个左右的输入表之外,对所有可能的表进行彻底的搜索已经不现实了,即使是六个或七个表,规划也可能需要很长时间。当输入表太多时,PostgreSQL planner将从穷举搜索切换到*遗传的*通过有限的可能性进行概率搜索。(切换阈值由[盖库\_门槛](runtime-config-query.html#GUC-GEQO-THRESHOLD)运行时参数。)基因搜索花费的时间更少,但不一定能找到最好的方案。 当查询涉及外部联接时,planner的自由度比普通(内部)联接要小。例如,考虑: ``` SELECT * FROM a LEFT JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id); ``` 虽然这个查询的限制表面上与前一个示例类似,但语义不同,因为在B和C的联接中,a的每一行都必须发出一行,而B和C的联接中没有匹配的行。因此,规划器在这里没有联接顺序的选择:它必须将B联接到C,然后将a联接到该结果。因此,与上一个查询相比,此查询计划所需的时间更少。在其他情况下,计划者可能能够确定多个联接顺序是安全的。例如,假设: ``` SELECT * FROM a LEFT JOIN b ON (a.bid = b.id) LEFT JOIN c ON (a.cid = c.id); ``` 先将A加入B或C是有效的。目前只有`完全连接`完全约束联接顺序。大多数实际案例涉及`左连接`或`右键连接`可以在某种程度上重新安排。 显式内部联接语法(`内部连接`, `交叉连接`,还是朴素`参加`)语义上与在中列出输入关系相同`从…起`,因此它不约束联接顺序。 尽管大多数`参加`不要完全约束联接顺序,可以指示PostgreSQL查询计划器处理所有`参加`子句作为约束联接顺序的条件。例如,这三个查询在逻辑上是等价的: ``` SELECT * FROM a, b, c WHERE a.id = b.id AND b.ref = c.id; SELECT * FROM a CROSS JOIN b CROSS JOIN c WHERE a.id = b.id AND b.ref = c.id; SELECT * FROM a JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id); ``` 但如果我们告诉规划者尊重`参加`第二种和第三种方法比第一种方法花费的时间更少。这种影响不值得担心,因为只有三个表,但它可以挽救许多表。 强制规划者遵循explicit制定的联接顺序`参加`s、 设定[参加\_崩溃\_限度](runtime-config-query.html#GUC-JOIN-COLLAPSE-LIMIT)将运行时参数设置为1。(以下讨论了其他可能的值。) 为了缩短搜索时间,不需要完全约束联接顺序,因为可以使用它`参加`在平面中的项目中的运算符`从…起`列表例如,考虑: ``` SELECT * FROM a CROSS JOIN b, c, d, e WHERE ...; ``` 具有`加入_崩溃_极限`=1时,这会强制计划程序在将A连接到B之前将其连接到其他表,但不会以其他方式限制其选择。在本例中,可能的联接顺序的数量减少了5倍。 以这种方式约束计划者的搜索对于减少计划时间和引导计划者选择好的查询计划都是一种有用的技术。如果计划员默认选择了一个错误的连接顺序,您可以通过`参加`语法——假设你知道更好的顺序,也就是说。建议进行实验。 影响计划时间的一个密切相关的问题是将子查询分解为父查询。例如,考虑: ``` SELECT * FROM x, y, (SELECT * FROM a, b, c WHERE something) AS ss WHERE somethingelse; ``` 这种情况可能是因为使用了包含连接的视图;景色很美`选择`规则将被插入到视图引用的位置,生成一个类似于上述的查询。通常,计划者会尝试将子查询折叠到父查询中,从而产生: ``` SELECT * FROM x, y, a, b, c WHERE something AND somethingelse; ``` 这通常会产生比单独规划子查询更好的计划。(例如,外部`哪里`条件可能是,将X连接到第一行可以消除A的许多行,从而避免形成子查询的完整逻辑输出。)但与此同时,我们增加了计划时间;在这里,我们有一个五路连接问题来代替两个单独的三路连接问题。由于可能性的数量呈指数级增长,这就产生了很大的不同。规划者试图避免陷入巨大的连接搜索问题,如果子查询超过`从_崩溃_极限` `从…起`项将导致父查询。您可以通过向上或向下调整此运行时参数来权衡计划时间和计划质量。 [从…起\_崩溃\_限度](runtime-config-query.html#GUC-FROM-COLLAPSE-LIMIT)和[参加\_崩溃\_限度](runtime-config-query.html#GUC-JOIN-COLLAPSE-LIMIT)它们的名称相似,因为它们做的事情几乎相同:一个控制规划器何时“展平”子查询,另一个控制规划器何时展平显式连接。通常你会`加入_崩溃_极限`相当于`从_崩溃_极限`(这样显式连接和子查询的作用类似)或`加入_崩溃_极限`到1(如果希望通过显式联接控制联接顺序)。但是,如果您试图微调计划时间和运行时间之间的权衡,则可能会对它们进行不同的设置。