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_DATEPRODUCT_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 最重要的面向。

更重要的是:資料庫以管線化方式執行,在讀完全部輸入之前就交出第一筆結果。這是下一章各種進階優化方法的前提。