| 场景一:JOIN连接消除 | | | - 子场景1.1:
SELECT DISTINCT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 = t2.c1; 其等价改写结果如下所示: SELECT DISTINCT t1.* FROM t1; - 子场景1.2:
SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c = t2.c GROUP BY t1.a, t1.b, t1.c; 其等价改写结果如下所示: SELECT t1.* FROM t1; |
| 子场景2:JOIN条件恒为FALSE的外连接消除 rulename: always_false_outer_join_elimination | - 对于左外连接或右外连接,若JOIN条件恒为FALSE,外表的每一行都无法和内表找到匹配行,连接后的投影列为外表每一行均返回一次并对内表投影列补NULL值,因此能消除该外连接。
- 规则默认开启状态:开启。
| (假设T1和T2表均只有一个属性列) SELECT * FROM t1 LEFT JOIN t2 ON 1 = 0; 其等价改写结果如下所示: SELECT t1.c1, NULL FROM t1; |
| 子场景3:同表同列的SEMI JOIN消除 rulename: same_table_semi_join_elimination | - 结合本场景SQL示例进行说明,当主查询和子查询引用的均为t1表且连接条件为同一列,其中t1表需为简单表,根据EXISTS子查询的语义,若t1.c1不全为NULL,则经过EXISTS子查询过滤后一定可返回t1.c1不为NULL的行;若t1.c1全为NULL,那么EXISTS子查询一定返回FALSE,此时结果集为空。该场景可以进行一些等价优化,消除SEMI JOIN部分。
- 规则默认开启状态:开启。
| SELECT * FROM t1 WHERE EXISTS(SELECT 1 FROM t1 tt WHERE t1.c1 = tt.c1); 其等价改写结果如下所示: SELECT * FROM t1 WHERE t1.c1 IS NOT NULL; |
| 场景二:LIMIT条件下压 | | - 如果外连接或交叉连接中没有WINDOW FUNCTION、DISTINCT、GROUP BY或HAVING,且WHERE条件或者ORDER BY仅和连接的一侧表有关时,可将LIMIT语句下压到连接的表的一侧(外连接)或多侧(多表交叉连接)。通过LIMIT 下推可有效减少连接的行数,从而降低计划执行的性能开销。
- 规则默认开启状态:开启。
| - 子场景1.1:
SELECT * FROM t1 LEFT JOIN t2 ON t1.c1 = t2.c1 LIMIT 1; 其等价改写结果如下所示: SELECT * FROM (SELECT * FROM t1 LIMIT 1) t LEFT JOIN t2 ON t.c1 = t2.c1 LIMIT 1; - 子场景1.2:
SELECT 1 FROM t1, t2 WHERE t1.c1 > 0 ORDER BY t1.c1 LIMIT 1; 其等价改写结果如下所示: SELECT 1 FROM (SELECT 1 FROM t1 WHERE t1.c1 > 0 ORDER BY t1.c1 LIMIT 1) tm1, (SELECT 1 FROM t2 LIMIT 1) tm2 LIMIT 1; |
| 场景三:冗余的DISTINCT消除 | | - 子场景1.1:
如果SELECT的投影列中只包含常量,则可以通过添加LIMIT 1约束消除DISTINCT,从而提前结束对数据的遍历。 规则默认开启状态:开启。 - 子场景1.2:
如果SELECT的投影列中只包含常量,且有OFFSET且OFFSET值>0时,则可以消除约束DISTINCT,整个SQL语句可以规约为约束条件为FALSE,提前结束SQL执行。 规则默认开启状态:关闭。 - 子场景1.3:
如果SELECT的投影列中只包含常量,且GROUP BY约束包含所有的投影列,没有HAVING条件、GROUPING SET时,可以消除GROUP BY增加LIMIT 1约束,从而提前结束对数据的遍历。 规则默认开启状态:关闭。 | - 子场景1.1:
SELECT DISTINCT 1,2 FROM t1; 其等价改写结果如下所示: SELECT 1,2 FROM t1 LIMIT 1; - 子场景1.2:
SELECT DISTINCT 1,2 FROM t1 OFFSET 1; 其等价改写结果如下所示: SELECT 1,2 FROM t1 WHERE FALSE; - 子场景1.3:
SELECT 1,2 FROM t1 GROUP BY 1,2; 其等价改写结果如下所示: SELECT 1,2 FROM t1 LIMIT 1; |
| | | - 子场景2.1:
SELECT DISTINCT c1, c2, c3 FROM t1; 其等价改写结果如下所示: SELECT c1, c2, c3 FROM t1; - 子场景2.2:
SELECT t1.a, t1.b, t1.c FROM t1 GROUP BY t1.a; 其等价改写结果如下所示: SELECT t1.a, t1.b, t1.c FROM t1; |
| 场景四:复杂场景扩展 | 子场景1:SEMI JOIN加速STREAM计划中的外连接 rulename: semi_join_accelerated_outer_join | - 当小表LEFT JOIN大表时,如果连接条件不是分布列,则大表必须重分布而不能BROADCAST小表,导致性能较差;此时可以将大表先和小表做一次半连接,过滤掉大部分数据后,再与小表做外连接,从而可以将小表BROADCAST。
- 规则默认开启状态:关闭。
| SELECT * FROM t2 small LEFT JOIN t1 big ON small.c1 = big.c1; 其等价改写结果如下所示: SELECT * FROM t2 small LEFT JOIN (SELECT * FROM t1 big WHERE big.c1 IN (SELECT c1 FROM t2)) fakebig ON small.c1 = fakebig.c1; |
| 子场景2:两个表达式子链接比较且存在子结构关系时消减子链接 rulename: subLink_compare_elimination | - 两个包含Count聚集函数的表达式子链接进行等值比较时,如果其中一个子链接可以表示为另一个子链接的子结构,且过滤条件仅相差一个EXISTS子链接条件时,可以对其进行优化。因为在此场景下,实际是想找到EXISTS条件返回FALSE的情况。在这种情况下两个count值是否相等取决于其他条件是否为TRUE。当其他条件为TRUE时,根据AND条件的语义,两个子链接的过滤条件相反,count()取值一定不同;否则取值相同。因此,对于这种场景可以使用NOT EXISTS等价改写消减子链接的数量。
- 规则默认开启状态:开启。
| SELECT t1.a, t1.b FROM t1,t2 WHERE t1.a = t2.a AND t1.b = t1.b AND (SELECT COUNT(*) FROM t1 as t11 WHERE t11.a = t1.a AND t11.b = t1.b) = (SELECT COUNT(*) FROM t1 AS t21 WHERE t21.a = t1.a AND t21.b = t1.b AND EXISTS(SELECT 1 FROM t3 WHERE t1.a = t3.a)); 其等价改写结果如下所示: SELECT t1.a, t1.b FROM t1,t2 WHERE t1.a = t2.a AND t1.b = t1.b AND NOT EXISTS (SELECT 1 FROM t1 as t11 WHERE t11.a = t1.a AND t11.b = t1.b AND NOT EXISTS(SELECT 1 FROM t3 WHERE t1.a = t3.a)); |
| | - 当主查询中包含常值谓词条件时,即表达式一侧是表的字段另一侧是常量,且子链接的查询条件中包含相同表字段时,则可以考虑将该常量条件下推到子链接中。
- 规则默认开启状态:关闭。
| - 子场景3.1:
SELECT t1.a, (SELECT sum(t2.a) FROM t2 WHERE t2.b = t1.b) FROM t1 WHERE t1.b = 1; 其等价改写结果如下所示: SELECT t1.a, (SELECT sum(t2.a) FROM t2 WHERE t2.b = t1.b AND t1.b = 1) FROM t1 WHERE t1.b = 1; - 子场景3.2:
SELECT t1.a FROM t1 WHERE t1.b = 1 AND (SELECT SUM(t2.a) FROM t2 WHERE t2.b = t1.b) = 0; 其等价改写结果如下所示: SELECT a FROM t1 WHERE b = 1 AND (SELECT sum(t2.a) FROM t2 WHERE t2.b = t1.b AND t1.b = 1) = 0; |
| 子场景4:相关表达式子链接转交叉连接 rulename: subLink_to_crossJoin | - 对于有聚集函数的等值表达式子链接,且子链接与常量比较时,可以将子链接提取为CTE表与主查询做交叉连接,在分布式场景特定的数据分布情况下,可以利用STREAM的BROADCAST提升性能。
- 规则默认开启状态:关闭。
| SELECT * FROM t1 WHERE t1.a = 1 AND (SELECT count(*) FROM t2 WHERE t2.b = t1.b AND t2.b = 1) = 3; 其等价改写结果如下所示: WITH cte AS (SELECT count(*) AS c, b FROM t2 WHERE t2.b = 1 GROUP BY b) SELECT * FROM t1, cte WHERE t1.a = 1 AND cte.b = t1.b AND cte.c = 3; |
| 子场景5:表达式子链接聚集函数为Count与0值比较时,可以消除聚集操作 rulename: count_zero_elimination | - 对于表达式相关子链接,如果子链接中的投影列是count()函数,且子链接与0做等值比较,则从语义上表示只有在过滤条件不满足时才为TRUE,因此将其等价改写为LEFT JOIN,并通过对内表的列进行IS NULL过滤,从而消除聚集函数和子计划。
- 规则默认开启状态:开启。
| SELECT t1.* FROM t1 WHERE t1.a = 1 AND (SELECT count(*) FROM t2 WHERE t2.b = t1.b AND t2.b = 1) = 0; 其等价改写结果如下所示: SELECT t1.* FROM t1 LEFT JOIN t2 ON t2.b = 1 AND t2.b = t1.b WHERE t1.a = 1 AND t2.b IS NULL; |
| 场景五:内连接唯一约束的自连接消除 | 基于DSL的规则引擎实现唯一约束的自连接消除需要如下两条规则: | - 当查询块中对多表进行主键或非空唯一约束上的内连接时,可通过查询重写规则消除冗余连接操作,从而减少查询中不必要的连接运算并提升查询整体性能。其原理是相同表在主键或非空唯一约束上的内连接具有严格的一对一匹配关系,因此,其结果集存在数据冗余,所有数据均可通过单次扫描原始表获取。
- 规则默认开启状态:开启。
| SELECT * FROM t1 tt1 INNER JOIN t1 tt2 ON tt1.a = tt2.a INNER JOIN t1 tt3 ON tt1.a = tt3.a WHERE tt1.b > 100; 其等价改写结果如下所示: SELECT tt1.*, tt1.*,tt1.* FROM t1 tt1 WHERE tt1.b > 100 AND tt1.a IS NOT NULL; |
| 场景六:ALL非相关子链接改写 | | - 典型SQL如下:
SELECT a FROM t1 WHERE a > ALL(SELECT a FROM t2); 对于上述SQL查询,表示遍历t1表并且返回满足过滤条件的行:即t1表的a列大于t2表a列的所有值。从等价逻辑可推导出,该条件等价于确保t1表的a列大于t2表a列的最大值。对于过滤条件或JOIN条件中的>ALL、>= ALL、< ALL、<= ALL类型的非相关子链接比较场景,均可通过等价改写来优化性能。 - 规则默认开启状态:关闭。
| - 子场景1.1:
SELECT a FROM t1 WHERE a > ALL(SELECT a FROM t2); 其等价改写结果如下所示: SELECT a FROM t1 WHERE CASE WHEN ((SELECT max(t2.a) AS a FROM t2)) IS NULL THEN true ELSE a > ((SELECT max(t2.a) AS a FROM t2)) END; - 子场景1.2:
SELECT a FROM t1 WHERE a < ALL(SELECT a FROM t2); 其等价改写结果如下所示: SELECT a FROM t1 WHERE CASE WHEN ((SELECT min(t2.a) AS a FROM t2)) IS NULL THEN true ELSE a < ((SELECT min(t2.a) AS a FROM t2)) END; - 子场景1.3:
SELECT * FROM t1 LEFT JOIN t4 ON t1.b = t4.b AND t1.a > ALL(SELECT a FROM t2); 其等价改写结果如下所示: SELECT * FROM t1 LEFT JOIN t4 ON t1.b = t4.b AND CASE WHEN ((SELECT max(t2.a) AS a FROM t2)) IS NULL THEN true ELSE t1.a > ((SELECT max(t2.a) AS a FROM t2)) END; - 子场景1.4:
SELECT * FROM t1 LEFT JOIN t4 ON t1.b = t4.b AND t1.a < ALL(SELECT a FROM t2); 其等价改写结果如下所示: SELECT * FROM t1 LEFT JOIN t4 ON t1.b = t4.b AND CASE WHEN ((SELECT min(t2.a) AS a FROM t2)) IS NULL THEN true ELSE t1.a < ((SELECT min(t2.a) AS a FROM t2)) END; 说明: 示例1.1及1.3中>ALL子链接的优化规则同样适用于>=连接的场景;同理,示例1.2及1.4使用<ALL的优化规则同样适用于<=连接的场景。 |
| 场景七:HAVING中MIN/MAX谓词下推过滤条件 | 子场景:支持HAVING中MIN/MAX谓词下推过滤条件 rulename: minmax_predicate_pushdown | - 在某些场景下,可将HAVING条件中的谓词下推至WHERE条件中,在保证结果一致性的前提下提前过滤数据,从而优化执行性能,其中一类场景为HAVING条件中包含MIN、MAX比较谓词,典型SQL示例如下:
SELECT a, min(b) FROM t1 GROUP BY a HAVING min(b) < 10; 分析上述SQL查询可知,其目标是筛选出min(b)<10分组后的聚合结果。若能在分组前过滤掉b<10的行,则可减少参与计算的数据量。基于此逻辑,可推导出新的谓词b<10,并将其下推至至过滤条件中。 - 规则默认开启状态:开启。
| - 示例1:
SELECT a, min(b) FROM t1 GROUP BY a HAVING min(b) < 10; 其等价改写结果如下所示: SELECT a, min(b) FROM t1 WHERE b < 10 GROUP BY a HAVING min(b) < 10; - 示例2:
SELECT a, max(b) FROM t1 GROUP BY a HAVING max(b) > 10; 其等价改写结果如下所示: SELECT a, max(b) FROM t1 WHERE b > 10 GROUP BY a HAVING max(b) > 10; 说明: 示例1中HAVING谓词中使用<连接的优化规则同样适用于<=连接的场景;同理,示例2中HAVING谓词使用>连接的优化规则同样适用于>=连接的场景。 |
| 场景八:LIMIT下推UNION ALL子查询 | 子场景:支持LIMIT下推UNION ALL子查询 rulename: unionall_limit_pushdown | | SELECT * FROM (SELECT * FROM t1 UNION ALL SELECT * FROM t2) LIMIT 2; 其等价改写结果如下所示: SELECT * FROM ((SELECT * FROM t1 LIMIT 2) UNION ALL (SELECT * FROM t2 LIMIT 2)) LIMIT 2; |
| 场景九:LIMIT下推子查询 | 子场景:支持LIMIT下推子查询 rulename: subquery_limit_pushdown | - 通常SQL语句的执行顺序如下:
FROM -> JOIN ON -> WHERE -> GROUP BY -> HAVING -> SELECT -> DISTINCT -> ORDER BY -> LIMIT 当执行至SELECT时,需对前序结果集中的每条数据进行计算,若涉及子链接等复杂计算,整体执行时间将成倍增加。从以上执行顺序可知,排序操作和LIMIT操作通常最后执行。从结果等价性角度分析,优先过滤冗余结果集后再对有效数据执行子链接计算,既可保证执行结果的正确性,又能有效提升执行性能,典型SQL示例如下: SELECT (SELECT min(a) FROM t2 WHERE t2.c >= t1.b) AS a1 from t1 INNER JOIN t3 ON t1.a = t3.a ORDER BY t1.a LIMIT 10; - 升级场景规则默认状态:关闭;新安装场景规则默认状态:开启。
| SELECT (SELECT min(a) FROM t2 WHERE t2.c >= t1.b) AS a1 FROM t1 INNER JOIN t3 ON t1.a = t3.a ORDER BY t1.a LIMIT 10; 其等价改写结果如下所示: SELECT (SELECT min(a) FROM t2 WHERE t2.c >= t.b) AS a1 FROM (SELECT t1.* FROM t1 INNER JOIN t3 ON t1.a = t3.a ORDER BY t1.a LIMIT 10) t ORDER BY t.a LIMIT 10; |