https://zhuanlan.zhihu.com/p/60380557express
子查詢,分兩種狀況,app
對於在From中的,稱爲‘derived table’,這種場景比較簡單函數
對於在select,where中的,scalar表達式,這是主要要考慮的對象,由於這種狀況cross了relational和scalar的處理優化
子查詢,最關鍵的區分,是關聯子查詢(Correlated Subquery)和非關聯子查詢(Non-correlated Subquery)ui
關聯子查詢,子查詢的執行,子查詢參數,依賴於外層父查詢輸出的屬性spa
非關聯子查詢,子查詢的執行,不依賴於外層父查詢的任何屬性值,這樣子查詢具備獨立性,可獨自求解scala
非關聯子查詢的優化很是簡單,由於能夠獨立求解,那就先求出並物化,帶入父查詢join便可3d
因此子查詢優化的核心就是,去關聯,de-correlate對象
從子查詢返回的數據分類,blog
標量(Scalar-valued)子查詢: 輸出就是一個標量
存在性檢測(Existential Test)子查詢:特指 EXISTS 的子查詢,返回一個布爾值
集合比較(Quantified Comparision)子查詢:特指 IN、SOME、ANY 的查詢,返回一個布爾值或Null,可能返回null
先看幾篇基礎的論文,
Microsoft的論文,
Parameterized Queries and Nesting Equivalencies - C Galindo-Legaria
Orthogonal Optimization of Subqueries and Aggregation - C Galindo-Legaria, M Joshi
Execution Strategies for SQL Subqueries - Mostafa Elhemali
這篇論文核心,是提出Apply算子,而且如何經過各類規則,把Apply算子轉換爲普通的join
例子中一個,標量子查詢的例子,
對於關聯子查詢,語義其實就是,對於outer表的每一行數據,都執行一遍子查詢,這個很是相似,function programming裏面的Apply算子
因此這裏就是是用Apply算子來,描述這種關聯性
Apply算子的定義,
對於關係表R,其中每一個r,帶入E,由於E是個參數化expression,把結果求並集
這裏其實對於R是要求Distinct,由於R中的r可能會重複,因此通常會加上一個算子去重
中間的A❌就表明apply算子,這裏和普通join同樣,根據對於E返回的Empty的處理,分爲,
看上面的例子,用Apply算子的結果,
若是outer表足夠小,而且有比較好的indexes的狀況下,Correlatied執行效率也很好的
可是大多數通常狀況下,Apply執行明顯效率很低,因此要基於Apply算子去進行優化,Apply算子是個中間狀態,比原來的形式更加容易使用關係代數優化
Apply的優化就,SubQuery unnesting或correlation removel,就是把Apply算子轉換爲regular join的過程,
下面看個最直接的例子,
先看個例子,
標量,關聯子查詢的例子
最直接的方法,直接Correlated execution,不必定低效,若是Outer表很小,而且有適當的索引
80年代,提出過一些優化的方案,
好比,先Outerjoin,再aggregate的方案
或者,先Aggregate,再Join
可是這些方案,不是系統的方法論,不一樣的狀況下,須要分析並使用不一樣的方法,
本文的方式是,把這些方法分解成,orthogonal, reusable primitives,用的時候能夠組合起來,用cost-estimation的方式,評估到底使用哪些primitives
能夠看出,分爲圖中的幾步,下面分別解釋一下各個步驟,
用Apply算子來抽象和替換parameterized execution of subexpressions
Apply算子的好處在於,去除relation和scalar節點間的mutual recursion
例子,左圖,查詢中有,scalar表達式,scalar表達式中又有子查詢,因此執行的時候,須要反覆在查詢計劃執行器和表達式執行器之間不斷切換,效率很低,並且隔着scalar表達式,無法用關係代數進行優化或reorder
因此用Apply算子變換到右圖,把子查詢從scalar表達式中remove掉,放到關係代數樹中
這裏用標量子查詢爲例,可是其餘的子查詢也是同樣的
這步只是對過程作了抽象,但實際上並無改變執行計劃
這裏還提到一種,SegmentApply算子
能夠認爲是batch版本的Apply算子
由於咱們能夠用column A對R進行分組,對於每一組,table-valued,去調用E
a就是Distinct(A)
也就是remove apply
作法, 不斷下推Apply,直到不關聯了,轉化爲regular join
The process consists of pushing down Apply in the operator tree, towards the leaves, until the right child of Apply is no longer parameterized off the left child.
可使用的轉換rules,
(1) (2),左右不相干的狀況下,apply能夠直接轉化成join
後面全部rules的目的,就是要轉換成知足 (1) (2)
這裏,(8)(9),比較難理解
首先,先看兩個概念,
Vector Aggregation,GroupBY聚合,group by A,aggregate with Function F
關鍵,表爲空的時候,返回也是empty
Scalar Aggregation,全局聚合,不指定A,aggregate with Function F
關鍵,總會有一行返回值,不會爲empty;而且返回值和Aggregate函數相關,若是是sum,返回null,若是是count,返回0
(8)中,columns(R)表示join key,把group by提到外面後,須要先按照join key作group by
(8)和(9)的差異就是,(9)是Scalar Aggregation,因此返回值對於empty會出現null,因此F’須要特殊處理,用非null的column
再者Scalar Aggre提到外面後變成Vector Aggre (group by join key)
下面看個例子,仍然是上面的SQL
首先,爲何是Scalar Aggre,由於這裏Group by join key,對於Apply算子,group by每次只apply到一個customer,因此是Scalar Aggre
用(9),把Scalar Aggre提出來,變成Vector Aggre
提出GroupBY後,剩下的能夠直接應用(2)消除Apply
最後,因爲最終的aggre結果須要非null,因此能夠簡化成inner join
Groupby能夠和其餘算子進行交換,好比filter,join等
GroupBy的Reorder是否會下降cost,這個須要cost model去判斷,好比先GroupBY,後Join,仍是先Join,後GroupBy
GroupBy和Filter Reorder
條件,if and only if all the columns used in the filter are functionally determined by the grouping columns in the input relation
過濾的columns,由GroupBy columns決定
GroupBy和Join Reorder
GroupBy pushdown 條件,
GroupBy Pullup的條件,
以TPCH-17爲例,
完成去關聯後的計劃以下,
這個計劃的問題是,咱們其實不用對全部Lineitem的rows都作這樣的聚合過濾,其實只是須要對過濾後的PartKey對應的lineitem作
因此這裏的方法是,把LineItem按照partkey進行group,每一個group叫作Segment,而後對Segment執行子查詢
叫作SegmentApply,
對於SegmentApply,下面要作的是,按照Part的條件過濾Segments,也就是要把外部的條件Push down到SegmentApply裏面,
push down的條件是,segment的完整性,join的條件以segment爲條件進行過濾,而不會過濾掉部分row
完成pushdown的結果以下,在SegmentApply以前,先會對LineItem作join進行過濾掉不須要的segment