GROUP BY ============================= GROUP BY 算子主要用于在 SQL 中进行分组聚合计算操作。 用于对数据进行分组的算法有 HASH 算法和 MERGE 算法,因此根据算法可以将 GROUP BY 算子分为两种:HASH GROUP BY 和 MERGE GROUP BY。执行计划生成时根据 SQL 优化器对于两种算子的代价评估,来选择使用哪种 GROUP BY 算子。 对于普通的聚合函数(SUM/MAX/MIN/AVG/COUNT/STDDEV)也是通过分配 GROUP BY 算子来完成,而对于只有聚合函数而不含有 GROUP BY 的 SQL,分配的是 SCALAR GROUP BY 算子,因此 GROUP BY 算子又可以分为三种:SCALAR GROUP BY、HASH GROUP BY 和 MERGE GROUP BY。 SCALAR GROUP BY ------------------------------------ 示例 1:含 SCALAR GROUP BY 算子的执行计划 ```javascript obclient>CREATE TABLE t1(c1 INT, c2 INT); Query OK, 0 rows affected (0.12 sec) obclient>INSERT INTO t1 VALUES(1, 1); Query OK, 1 rows affected (0.12 sec) obclient>INSERT INTO t1 VALUES(2, 2); Query OK, 1 rows affected (0.12 sec) obclient>INSERT INTO t1 VALUES(3, 3); Query OK, 1 rows affected (0.12 sec) Q1: obclient> EXPLAIN SELECT SUM(c1) FROM t1\G; *************************** 1. row *************************** Query Plan: | ======================================== |ID|OPERATOR |NAME|EST. ROWS|COST| ---------------------------------------- |0 |SCALAR GROUP BY| |1 |37 | |1 | TABLE SCAN |T1 |3 |37 | ======================================== Outputs & filters: ------------------------------------- 0 - output([T_FUN_SUM(T1.C1)]), filter(nil), group(nil), agg_func([T_FUN_SUM(T1.C1)]) 1 - output([T1.C1]), filter(nil), access([T1.C1]), partitions(p0) ``` 上述示例中,Q1 查询的执行计划展示中的 outputs \& filters 中详细列出了 SCALAR GROUP BY 算子的输出信息如下: | **信息名称** | **含义** | |----------|------------------------------------------------------------------------------| | output | 该算子输出的表达式。 | | filter | 该算子上的过滤条件。 由于示例中 SCALAR GROUP BY 算子未设置 filter,所以为 nil。 | | group | 需要进行分组的列。 例如,Q1 查询中是 SCALAR GROUP BY 算子,所以为 nil。 | | agg_func | 所涉及的聚合函数。 例如,Q1 查询是计算表 t1 的 c1 列数据之和,因此为 `T_FUN_SUM(t1.c1)`。 | HASH GROUP BY ---------------------------------- 示例 2:含 HASH GROUP BY 算子的执行计划 ```javascript Q2: obclient>EXPLAIN SELECT SUM(c2) FROM t1 GROUP BY c1 HAVING SUM(c2) > 2\G; *************************** 1. row *************************** Query Plan: | ====================================== |ID|OPERATOR |NAME|EST. ROWS|COST| -------------------------------------- |0 |HASH GROUP BY| |1 |40 | |1 | TABLE SCAN |T1 |3 |37 | ====================================== Outputs & filters: ------------------------------------- 0 - output([T_FUN_SUM(T1.C2)]), filter([T_FUN_SUM(T1.C2) > 2]), group([T1.C1]), agg_func([T_FUN_SUM(T1.C2)]) 1 - output([T1.C1], [T1.C2]), filter(nil), access([T1.C1], [T1.C2]), partitions(p0) ``` 上述示例中,Q2 查询的执行计划展示中的 outputs \& filters 详细列出了 HASH GROUP BY 算子的输出信息如下: | **信息名称** | **含义** | |----------|------------------------------------------------------------------------------| | output | 该算子输出的表达式。 | | filter | 该算子上的过滤条件。 由于设置要求分组后的 c2 列求和大于 2,因此为 `T_FUN_SUM(t1.c2) > 2`。 | | group | 需要进行分组的列。 例如,Q2 查询是 HASH GROUP BY 算子,所以为 nil。 | | agg_func | 所涉及的聚合函数。 例如,Q2 查询中计算表 t1 的 c1 列之和,因此为 `T_FUN_SUM(t1.c1)`。 | **说明** HASH GROUP BY 算子将会保证在执行时采用 HASH 算法进行分组。 MERGE GROUP BY ----------------------------------- 示例 3:含 MERGE GROUP BY 算子的执行计划 ```javascript Q3: obclient>EXPLAIN SELECT /*+NO_USE_HASH_AGGREGATION*/SUM(c2) FROM t1 GROUP BY c1 HAVING SUM(c2) > 2\G; *************************** 1. row *************************** Query Plan: | ======================================= |ID|OPERATOR |NAME|EST. ROWS|COST| --------------------------------------- |0 |MERGE GROUP BY| |1 |45 | |1 | SORT | |3 |44 | |2 | TABLE SCAN |T1 |3 |37 | ======================================= Outputs & filters: ------------------------------------- 0 - output([T_FUN_SUM(T1.C2)]), filter([T_FUN_SUM(T1.C2) > 2]), group([T1.C1]), agg_func([T_FUN_SUM(T1.C2)]) 1 - output([T1.C1], [T1.C2]), filter(nil), sort_keys([T1.C1, ASC]) 2 - output([T1.C1], [T1.C2]), filter(nil), access([T1.C1], [T1.C2]), partitions(p0) ``` 上述示例中,Q3 查询的执行计划展示中的 outputs \& filters 中详细列出了 MERGE GROUP BY 算子的信息,可以看出相同的 SQL 生成执行计划时选择了 MERGE GROUP BY 算子,其算子基本信息都是相同的,最大的区别是在执行的时候选择的分组算法不一样。同时,这里的 2 号算子 TABLE SCAN 返回的结果是一个无序结果,而 GROUP BY 算法采用的是 MERGE GROUP BY,因此必须分配一个 SORT 算子。 **注意** NO_USE_HASH_AGGREGATION 和 USE_HASH_AGGREGATION 的 HINT 可以用于控制 GROUP BY 算子选择何种算法进行分组。