一文終結SQL 子查詢優化
子查詢(Subquery)的優化一直以來都是 SQL 查詢優化中的難點之一。關聯子查詢的基本執行方式類似於 Nested-Loop,但是這種執行方式的效率常常低到難以忍受。當資料量稍大時,必須在優化器中對其進行去關聯化(Decoorelation 或 Unnesting),將其改寫為類似於 Semi-Join 這樣的更高效的運算元。
前人已經總結出一套完整的方法論,理論上能對任意一個查詢進行去關聯化。本文結合 SQL Server 以及 HyPer 的幾篇經典論文,由淺入深地講解一下這套去關聯化的理論體系。它們二者所用的方法大同小異,基本思想是想通的。
本文的例子都基於 TPC-H 的表結構,這裡 有一份供你參考。
子查詢簡介
子查詢是定義在 SQL 標準中一種語法,它可以出現在 SQL 的幾乎任何地方,包括 SELECT, FROM, WHERE 等子句中。
總的來說,子查詢可以分為關聯子查詢(Correlated Subquery)和非關聯子查詢(Non-correlated Subquery)。後者非關聯子查詢是個很簡單的問題,最簡單地,只要先執行它、得到結果集並物化,再執行外層查詢即可。下面是一個例子:
SELECT c_count, count(*) AS custdist FROM ( SELECT c_custkey, count(o_orderkey) AS c_count FROM CUSTOMER LEFT OUTER JOIN ORDERS ON c_custkey = o_custkey AND o_comment NOT LIKE '%pending%deposits%' GROUP BY c_custkey ) c_orders GROUP BY c_count ORDER BY custdist DESC, c_count DESC;
▲ TPCH-13 是一個非關聯子查詢
非關聯子查詢不在本文討論範圍之列,除非特別宣告,以下我們說的子查詢都是指關聯子查詢。
關聯子查詢的特別之處在於,其本身是不完整的:它的閉包中包含一些外層查詢提供的引數。顯然,只有知道這些引數才能執行該查詢,所以我們不能像對待非關聯子查詢那樣。
根據產生的資料來分類,子查詢可以分成以下幾種:
標量(Scalar-valued) 子查詢:輸出一個只有一行一列的結果表,這個標量值就是它的結果。如果結果為空(0 行),則輸出一個 NULL。但是注意,超過 1 行結果是不被允許的,會產生一個執行時異常。
標量子查詢可以出現在任意包含標量的地方,例如 SELECT、WHERE 等子句裡。下面是一個例子:
SELECT c_custkey FROM CUSTOMER WHERE 1000000 < ( SELECT SUM(o_totalprice) FROM ORDERS WHERE o_custkey = c_custkey )
▲ Query 1: 一個出現在 WHERE 子句中的標量子查詢,關聯引數用紅色字型標明瞭
SELECT o_orderkey, ( SELECT c_name FROM CUSTOMER WHERE c_custkey = o_custkey ) AS c_name FROM ORDERS
▲ Query 2: 一個出現在 SELECT 子句中的標量子查詢
存在性檢測(Existential Test) 子查詢:特指 EXISTS 的子查詢,返回一個布林值。如果出現在 WHERE 中,這就是我們熟悉的 Semi-Join。當然,它可能出現在任何可以放布林值的地方。
SELECT c_custkey FROM CUSTOMER WHERE c_nationkey = 86 AND EXISTS( SELECT * FROM ORDERS WHERE o_custkey = c_custkey )
▲ Query 3: 一個 Semi-Join 的例子
集合比較(Quantified Comparision) 子查詢:特指 IN、SOME、ANY 的查詢,返回一個布林值,常用的形式有:x = SOME(Q) (等價於 x IN Q)或 X <> ALL(Q)(等價於 x NOT IN Q)。同上,它可能出現在任何可以放 原始碼交易 布林值的地方。
SELECT c_name FROM CUSTOMER WHERE c_nationkey <> ALL (SELECT s_nationkey FROM SUPPLIER)
▲ Query 4: 一個集合比較的非關聯子查詢
原始執行計劃
我們以 Query 1 為例,直觀地感受一下,為什麼說關聯子查詢的去關聯化是十分必要的。
下面是 Query 1 的未經去關聯化的原始查詢計劃(Relation Tree)。與其他查詢計劃不一樣的是,我們特地畫出了表示式樹(Expression Tree),可以清晰地看到:子查詢是實際上是掛在 Filter 的條件表示式下面的。
img實際執行時,查詢計劃執行器(Executor)在執行到 Filter 時,呼叫表示式執行器(Evaluator);由於這個條件表示式中包含一個標量子查詢,所以 Evaluator 又會呼叫 Executor 計算標量子查詢的結果。
這種 Executor - Evaluator - Executor 的交替呼叫十分低效!考慮到 Filter 上可能會有上百萬行資料經過,如果為每行資料都執行一次子查詢,那查詢執行的總時長顯然是不可接受的。
Apply 運算元
上文說到的 Relation - Expression - Relation 這種交替引用不僅執行效能堪憂,而且,對於優化器也是個麻煩的存在——我們的優化規則都是在匹配並且對 Relation 進行變換,而這裡的子查詢卻藏在 Expression 裡,令人無從下手。
為此,在開始去關聯化之前,我們引入 Apply 運算元:
Apply 運算元(也稱作 Correlated Join)接收兩個關係樹的輸入,與一般 Join 不同的是,Apply 的 Inner 輸入(圖中是右子樹)是一個帶有引數的關係樹。
Apply 的含義用下圖右半部分的集合表示式定義:對於 Outer Relation RR 中的每一條資料 rr,計算 Inner Relation E(r)E(r),輸出它們連線(Join)起來的結果 r⊗E(r)r⊗E(r)。Apply 的結果是所有這些結果的並集(本文中說的並集指的是 Bag 語義下的並集,也就是 UNION ALL)。
“
Apply 是 SQL Server 的命名,它在 HyPer 的文章中叫做 Correlated Join。它們是完全等價的。考慮到 SQL Server 的文章發表更早、影響更廣,本文中都沿用它的命名。
根據連線方式(⊗⊗)的不同,Apply 又有 4 種形式:
Cross Apply A×A×:這是最基本的形式,行為剛剛我們已經描述過了;
Left Outer Apply ALOJALOJ:即使 E(r)E(r) 為空,也生成一個 r∘{NULLs}r∘{NULLs}。
Semi Apply A∃A∃:如果 E(r)E(r) 不為空則返回 rr,否則丟棄;
Anti-Semi Apply A∄A∄:如果 E(r)E(r) 為空則返回 rr,否則丟棄;
我們用剛剛定義的 Apply 運算元來改寫之前的例子:把子查詢從 Expression 內部提取出來。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69990088/viewspace-2754884/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 優化sql查詢速度優化SQL
- exists與in子查詢優化優化
- 十七、Mysql之SQL優化查詢MySql優化
- SQL查詢的:子查詢和多表查詢SQL
- 一文讀懂MySQL的索引結構及查詢優化MySql索引優化
- mysql查詢效能優化總結MySql優化
- 《MySQL慢查詢優化》之SQL語句及索引優化MySql優化索引
- SQL查詢總結SQL
- PostgreSQL 原始碼解讀(28)- 查詢語句#13(查詢優化-上拉子連結#3)SQL原始碼優化
- PostgreSQL 原始碼解讀(26)- 查詢語句#11(查詢優化-上拉子連結#1)SQL原始碼優化
- PostgreSQL 原始碼解讀(27)- 查詢語句#12(查詢優化-上拉子連結#2)SQL原始碼優化
- SQL語言(結構化查詢語言)SQL
- Sql語句本身的優化-定位慢查詢SQL優化
- SQL優化之多表關聯查詢-案例一SQL優化
- Mysql優化系列之——優化器對子查詢的處理MySql優化
- PostgreSQL 原始碼解讀(29)- 查詢語句#14(查詢優化-上拉子查詢)SQL原始碼優化
- MySQL 索引及查詢優化總結MySql索引優化
- 查詢優化優化
- 關於樹結構的查詢優化,及許可權樹的查詢優化優化
- MySQL exists關聯子查詢SQL效能及其低下最佳化之等值子查詢轉換MySql
- 如何調優 Oracle SQL系列文章:查詢優化器介紹OracleSQL優化
- SQL語言基礎(子查詢)SQL
- sql-server不相關子查詢SQLServer
- sql-server相關子查詢SQLServer
- SQL(Structured Query Language,結構化查詢語言)SQLStruct
- MySql常用30種SQL查詢語句優化方法MySql優化
- pgsql查詢優化之模糊查詢SQL優化
- 優化-mysql子查詢索引失效問題解決優化MySql索引
- HBase查詢優化優化
- Oracle in 查詢優化Oracle優化
- join 查詢優化優化
- MySQL查詢優化MySql優化
- 什麼是SQL 語句中相關子查詢與非相關子查詢SQL
- ORACLE_OCP之SQL_子查詢OracleSQL
- SQL連線查詢優化[姊妹篇.第五彈]SQL優化
- MySQL 優化五(關聯查詢子查詢以及 in 的效率問題)(高階篇)MySql優化
- StoneDB 子查詢最佳化
- mysql查詢優化檢查 explainMySql優化AI