子查詢折疊
子查詢折疊是將SQL語(yǔ)句中多個(gè)子查詢根據(jù)某種規(guī)則進(jìn)行折疊,以減少SQL語(yǔ)句中子查詢的數(shù)量,從而加快SQL語(yǔ)句執(zhí)行速率的一種子查詢優(yōu)化手段。本文介紹了子查詢折疊的背景知識(shí)、子查詢折疊的折疊原理、使用方法以及示例等內(nèi)容。
背景知識(shí)
子查詢類型
PolarDB MySQL版支持的子查詢類型見下表:
子查詢類型 | 算子關(guān)鍵字 | 比較算子 | 備注 |
EXISTS |
| 無(wú) | 無(wú) |
IN |
| 無(wú) | 無(wú) |
ANY | 無(wú) | =、!=、<、<=、<、>= | 如 |
ALL | 無(wú) | =、!=、<、<=、<、>= | 如 |
單行標(biāo)量子查詢 | 如 |
同類型子查詢:如果子查詢類型與對(duì)應(yīng)的算子關(guān)鍵字一致,則稱之為同類型子查詢。如兩個(gè)子查詢都是
EXISTS
,或者兩個(gè)子查詢都是> ANY
,則為同類型子查詢。互斥子查詢:如果子查詢類型與對(duì)應(yīng)的算子關(guān)鍵字語(yǔ)義相反,則稱之為互斥子查詢。如
EXISTS
與NOT EXISTS
即為一對(duì)互斥子查詢,IN
與NOT IN
為一對(duì)互斥子查詢。更多互斥子查詢參考如下表:子查詢
互斥子查詢
EXISTS
NOT EXISTS
IN
NOT IN
= ANY
!= ALL
!= ANY
= ALL
< ANY
>= ALL
或> ALL
<= ANY
> ALL
> ANY
<= ALL
或< ALL
>= ANY
< ALL
子查詢包含關(guān)系
子查詢的右側(cè)結(jié)果集是一個(gè)集合。集合有三種包含關(guān)系:左子集、右子集、相等。如果集合沒(méi)有包含關(guān)系,則稱之為不可比較。下文以左子集為例進(jìn)行介紹。
左子集:若子查詢左側(cè)的集合是右側(cè)集合的子集,則稱為左子集。示例如下:
SELECT a
FROM t
WHERE EXISTS (
SELECT /*+ subq1 */ t2.a
FROM t2
WHERE t2.a > 10
)
AND EXISTS (
SELECT /*+ subq2 */ t2.a
FROM t2
)
從上述示例中可以看出,左側(cè)subq1
的條件更嚴(yán)格,結(jié)果集更小,是右側(cè)subq2
集合的子集,所以稱為左子集。
子查詢折疊功能概述
折疊的對(duì)象可以出現(xiàn)在WHERE
、HAVING
、JOIN ON
條件的任何位置上,子查詢同時(shí)出現(xiàn)在AND
/OR
邏輯算子下。
子查詢可以是EXISTS
、IN
子查詢,ALL
或ALL子查詢,支持所有的運(yùn)算算子。
同類型子查詢
如果兩個(gè)子查詢的集合具備包含關(guān)系,則消除其中一個(gè)。具體規(guī)則如下:
子查詢間邏輯運(yùn)算 | 左右子查詢類型 | 子查詢包含關(guān)系 | 限制 | 折疊類型 | 折疊說(shuō)明 |
AND | 同為EXISTS、IN、ANY、ALL | 左子集、相等 | 無(wú) | 消除 | 消除右子集,保留左子查詢。 參考示例一:AND條件下子查詢消除。 |
右子集 | 無(wú) | 消除 | 消除左子集,保留右子查詢。 | ||
同為NOT EXISTS、NOT IN、!= ALL | 不可比較 |
| 合并(不總是最優(yōu)) 說(shuō)明 不總是最優(yōu)是指折疊后執(zhí)行效率可能比折疊前差,并不能保證一定是一個(gè)正收益優(yōu)化。實(shí)際上需要配合基于CBQT組件才能決定是否應(yīng)用當(dāng)前規(guī)則。 | 合并二者的WHERE或HAVING條件,合并為一個(gè)新的子查詢。 參考示例一:AND條件下的子查詢合并。 | |
OR | 同為EXISTS、IN、ANY、ALL | 左子集、相等 | 無(wú) | 消除 | 消除左子集,保留右子集。參考示例二:OR條件下子查詢消除。 |
右子集 | 無(wú) | 消除 | 消除右子集,保留左子集。 | ||
同為EXISTS、IN、ANY | 不可比較 |
| 合并(不總是最優(yōu)) | 合并二者的WHERE或HAVING條件,合并為一個(gè)新的子查詢。 參考示例二:OR條件下的子查詢合并。 |
互斥子查詢
如果兩個(gè)子查詢的集合具備包含關(guān)系,依賴于邏輯運(yùn)算上下文可以整體改寫為TRUE或FALSE,或者將兩個(gè)子查詢合二為一,生成一個(gè)新的子查詢。具體規(guī)則如下:
子查詢間邏輯運(yùn)算 | 左右子查詢類型 | 子查詢包含關(guān)系 | 限制 | 折疊類型 | 折疊說(shuō)明 |
AND |
| 左子集、相等 | 無(wú) | 消除 | 將AND條件改寫為FALSE。 |
EXISTS與NOT EXISTS | 右子集 |
| 合并(不總是最優(yōu)) | 合并集合,增加 | |
| 左子集、相等 | 無(wú) | 消除 | 將AND條件改寫為FALSE。 | |
| 右子集 |
| 合并(總是最優(yōu)) | 合并集合,增加LNNVL算子 折疊總是最優(yōu),默認(rèn)折疊。 | |
OR | EXISTS與NOT EXISTS | 右子集 | 無(wú) | 消除 | 將OR條件改寫為TRUE。 |
使用前提
集群版本需為PolarDB MySQL版8.0版本且修訂版本需為8.0.2.2.23或以上。如何查看集群版本,請(qǐng)參見查詢版本號(hào)。
使用方法
您可以通過(guò)將參數(shù)loose_polar_optimizer_switch
的值設(shè)置為coalesce_subquery=on
來(lái)開啟子查詢折疊功能,以及將參數(shù)force_coalesce_subquery
的值設(shè)置為ON來(lái)開啟子查詢合并功能。設(shè)置參數(shù)值的具體操作請(qǐng)參見設(shè)置集群參數(shù)和節(jié)點(diǎn)參數(shù)。
參數(shù)名稱 | 級(jí)別 | 描述 |
loose_polar_optimizer_switch | Global | 僅開啟或關(guān)閉子查詢折疊功能。默認(rèn)不做子查詢合并。 取值范圍如下:
|
force_coalesce_subquery | Global | 開啟或關(guān)閉子查詢合并功能,子查詢折疊規(guī)則表格中的不總是最優(yōu)折疊會(huì)強(qiáng)制執(zhí)行。 取值范圍如下:
說(shuō)明
|
示例
同類型子查詢消除
示例一:AND條件下子查詢消除
SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c2 = 0) --子查詢1
AND EXISTS (SELECT 1 FROM t2); --子查詢2
其中,子查詢1是子查詢2的子集,因此直接消除子查詢2。消除后的SQL語(yǔ)句如下:
SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c2 = 0);
示例二:OR條件下子查詢消除
SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c2 = 0) --子查詢1
or EXISTS (SELECT 1 FROM t2); --子查詢2
其中,子查詢1被消除掉,OR條件改寫為EXISTS (SELECT 1 FROM t2)
,保留大集合。消除后的SQL語(yǔ)句如下:
SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2);
同類型子查詢合并
示例一:AND條件下的子查詢合并
SELECT * FROM t1 WHERE NOT EXISTS (SELECT t1.a AS f FROM t1 WHERE a >10 AND b < 10)
AND NOT EXISTS (SELECT a FROM t1 WHERE a > 10 AND c <3);
合并后的SQL語(yǔ)句如下:
SELECT * FROM t1 WHERE NOT EXISTS (SELECT t1.a AS f FROM t1 WHERE a >10 AND (b < 10 OR c <3);
示例二:OR條件下的子查詢合并
SELECT * FROM t1 WHERE EXISTS (SELECT t1.a AS f FROM t1 WHERE a >10 AND b < 10)
OR EXISTS (SELECT a FROM t1 WHERE a > 10 AND c <3);
合并后的SQL語(yǔ)句如下:
SELECT * FROM t1 WHERE EXISTS (SELECT t1.a AS f FROM t1 WHERE a >10 AND (b < 10 OR c <3);
互斥子查詢消除
示例一:EXISTS互斥類型沖突
適用場(chǎng)景:EXISTS與NOT EXISTS、IN或NOT IN
SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c1 = 0) --子查詢1
AND NOT EXISTS (SELECT 1 FROM t2); --子查詢2
將AND條件改寫為FALSE,改寫后的SQL語(yǔ)句如下:
SELECT * FROM t1 WHERE false;
示例二:ANY或ALL互斥類型沖突
適用場(chǎng)景:
>ANY與<ALL、<=ALL
<ANY與>ALL、>=ALL
SELECT * FROM t1 WHERE t1.c1 > ANY (SELECT c1 FROM t2 WHERE c1 > 10 AND c2 > 1)
AND t1.c1 < ALL (SELECT c1 FROM t2 WHERE c1 > 10);
將AND條件改寫為FALSE,改寫后的SQL語(yǔ)句如下:
SELECT * FROM t1 WHERE false; //ANY是ALL集合的子集
示例三:OR條件下EXISTS查詢消除
SELECT * FROM t1 WHERE exists (SELECT 1 FROM t2 ) --子查詢1
OR NOT exists (SELECT 1 FROM t2 WHERE c1 = 0); --子查詢2
將OR條件改寫為TRUE,改寫后的SQL語(yǔ)句如下:
SELECT * FROM t1 WHERE true; //子查詢2是子查詢1的子集
示例四:EXISTS互斥子查詢合并
SELECT * FROM t1 WHERE EXIST (SELECT 1 FROM t2) --子查詢1
AND NOT EXIST (SELECT 1 FROM t2 WHERE c2 = 0); --子查詢2
合并集合,增加HAVING SUM(CASE WHEN extra_cond THEN 1 ELSE 0 END) ==0
條件。合并后的SQL語(yǔ)句如下:
SELECT * FROM t1 WHERE EXIST (SELECT 1 FROM t2 HAVING SUM (CASE WHEN extra_cond THEN 1 ELSE 0 END) ==0);
合并不總是最優(yōu)的,您需要基于代價(jià)選擇是否進(jìn)行折疊,如確認(rèn)改寫較優(yōu),需將參數(shù)force_coalesce_subquery
的值設(shè)置為ON來(lái)開啟子查詢合并功能。
基于TPCH Q21熱數(shù)據(jù),開啟子查詢折疊功能前后的查詢耗時(shí)如下,耗時(shí)短表示改寫更優(yōu):
示例五:ANY或ALL互斥子查詢合并
適用場(chǎng)景:
IN與NOT IN,并且NOT IN集合更小,是左側(cè)子集。
=ANY與 != ALL ,并且ALL集合更小,是左側(cè)子集。
SELECT * FROM t1 WHERE t1.c1 = ANY (SELECT c1 FROM t2 WHERE c1 > 10) AND
t1.c1 != ALL (SELECT c1 FROM t2 WHERE c1 > 100);
合并集合,增加LNNVL算子。合并后的SQL語(yǔ)句如下:
SELECT * FROM t1 WHERE t1.c1 =
ANY (SELECT c1 FROM t2 WHERE c1 > 10 AND LNNVL(c1 >100));