一次在单位工作群里看到了这样一段对话,觉着可以学习一下,特作此记录。先上一个SQL:
SELECT DISTINCT C2.GT_PID AS MDCODE, '2022Y00121' AS DATATIME FROM N35R_YB21007 C1,TMP_GATHER_TABLE C2 WHERE NOT EXISTS (SELECT 1 FROM N35R_YB21007 t3 WHERE t3.MDCODE=C2.GT_PID AND t3.DATATIME=? AND t3.MD_GCORGTYPE=? AND t3 MD_CURRENCY=? AND t3 MD_GCADJTYPE=? ) AND C2.EXECUTION_ID=? |
来看他的执行计划:
可以看到这里的驱动表走了一个inner join,然后cost很高,达到了1074。而一共得到的结果集的行数是556381行。整体走了一个哈希右半连接。
对于not exists的优化思路,一般我总结为两种(其中一种就是群里这个大神提到的)。一种为将not exists改为left join。保证驱动结果集相对较小,改写时需要注意在最后加上一个被驱动表连接列 is null的关键字。这用于保证结果集在改写SQL后的相等性。
这种改写方法可以就是解决当外部表过大,每次刷not exists的结果集时造成的sql执行损耗。但重点在于达梦数据库有一个SPL的扁平化特性。可以尝试在不修改SQL的前提下,让相关子查询结果集转变为一个固定的临时表结果集。而后和外表进行关联。保证了子查询不会一直因为外查询结果集过大而一直刷子查询结果集。相关参数为:enable_rq_to_nonref_spl
查询官方手册得到如下讲解:
相关查询表达式转化为非相关查询表达式,目的在于相关查询表达式的执行处理由之前的平坦化方式转化为一行一行处理,类似 ORACLE 的每行处理机制。 0:不启用该优化; 1:对查询项中出现的相关子查询表达式进行优化处理; 2:对查询项和 WHERE 表达式中出现的相关子查询表达式进行优化处理; 4:相关查询采用 SPL 方式去相关性后,可以作为单表过滤条件 支持使用上述有效值的组合值,如 3 表示同时进行 1 和 2 的优化 |
这里的非相关查询就是说,子查询的结果集不会因为父查询的原因而发生变化。这是可以理解的,因为子查询的结果集已经存在了临时表里,不会再更,即,不会多次刷表。
看群里大神将这个值增大到了7,即/*+ enable_rq_to_nonref_spl(7) */。用于查询上的优化。
最后总结下这篇文章究竟说了个啥:
- not exists的优化思路是啥(实际left join,方式可以加入并行,不做赘述)?
- 什么是相关子查询,什么是非相关子查询?
- enable_rq_to_nonref_spl参数(可写成hints)详解。
达梦数据库技术社区:https://eco.dameng.com