SQL 資料庫使用兩種截然不同的 group by 演算法:
- 雜湊演算法(hash):在一張暫時的雜湊表中彙總輸入記錄,處理完所有輸入後回傳該表作為結果。
- 排序/分組演算法(sort/group):先依分組鍵排序輸入資料,讓同一組的列緊鄰相接,之後只需彙總即可。
一般而言兩者都需要具體化中間狀態,因此無法管線化執行。但排序/分組演算法可以利用索引省去排序,從而達成管線化的 group by。
MySQL 5.6 不使用雜湊演算法,但下述針對排序/分組演算法的優化依然適用。
管線化的 GROUP BY#
以下查詢取出昨天依 PRODUCT_ID 分組的營收:
SELECT product_id, sum(eur_value)
FROM sales
WHERE sale_date = TRUNC(sysdate) - INTERVAL '1' DAY
GROUP BY product_id;有了上一節建在 SALE_DATE 與 PRODUCT_ID 上的索引,排序/分組演算法更為合適——INDEX RANGE SCAN 自動就以所需順序交出列。資料庫因而不必具體化、也不需明確排序,group by 以管線化方式執行:
---------------------------------------------------------------
|Id |Operation | Name | Rows | Cost |
---------------------------------------------------------------
| 0 |SELECT STATEMENT | | 17 | 192 |
| 1 | SORT GROUP BY NOSORT | | 17 | 192 |
| 2 | TABLE ACCESS BY INDEX ROWID| SALES | 321 | 192 |
|*3 | INDEX RANGE SCAN | SALES_DT_PR | 321 | 3 |
---------------------------------------------------------------Oracle 執行計畫以
NOSORT附註標示管線化的SORT GROUP BY操作。其他資料庫的執行計畫則根本不會提到任何排序操作。
前提條件#
管線化 group by 的前提與管線化 order by 相同,只差在沒有 ASC / DESC 修飾詞的問題——用 ASC/DESC 定義索引理應不影響管線化 group by 的執行,NULLS FIRST/LAST 亦然。
儘管如此,仍有資料庫無法妥善地把 ASC/DESC 索引用於管線化 group by:
- PostgreSQL:必須加上 order by 子句,帶
NULLS LAST排序的索引才能用於管線化 group by。- Oracle:當管線化 group by 後面跟著 order by 時,無法反向讀取索引。
擴大範圍同樣會破壞它#
若像先前管線化 order by 的例子那樣,把查詢擴大到「昨天以來的所有銷售」,管線化 group by 會因同樣的理由失效:INDEX RANGE SCAN 交出的列不再依分組鍵排序。
SELECT product_id, sum(eur_value)
FROM sales
WHERE sale_date >= TRUNC(sysdate) - INTERVAL '1' DAY
GROUP BY product_id;---------------------------------------------------------------
|Id |Operation | Name | Rows | Cost |
---------------------------------------------------------------
| 0 |SELECT STATEMENT | | 24 | 356 |
| 1 | HASH GROUP BY | | 24 | 356 |
| 2 | TABLE ACCESS BY INDEX ROWID| SALES | 596 | 355 |
|*3 | INDEX RANGE SCAN | SALES_DT_PR | 596 | 4 |
---------------------------------------------------------------Oracle 改用雜湊演算法。
雜湊演算法的優勢是只需緩衝彙總後的結果,而排序/分組演算法必須具體化完整的輸入集。換言之:雜湊演算法需要的記憶體更少。
重點不在快,而在管線化#
與管線化 order by 一樣,執行得快並非管線化 group by 最重要的面向。
更重要的是:資料庫以管線化方式執行,在讀完全部輸入之前就交出第一筆結果。這是下一章各種進階優化方法的前提。