grouping sets 用法

本文介绍如何使用SQL的GROUP BY和GROUPING SETS来获取不同级别的数据汇总,包括部门和职位级别的薪资总额、入职日期的最大值和最小值等,并展示如何通过简单的语法变化获得这些汇总信息。

Let's say you are running queries against the Employees table of the sample HR schema provided by Oracle.

SQL> SELECT count(*) FROM Employees;

COUNT(*)
----------
107
Now, we want to get a total of salaries for each job in each department. We also want the max and min hire dates. We can do this with a simple query:

SELECT department_id,
job_id,
count(*),
sum(salary),
max(hire_date),
min(hire_date)
FROM Employees
GROUP BY department_id, job_id

/
DEPARTMENT_ID JOB_ID COUNT(*) SUM(SALARY) MAX(HIRE_ MIN(HIRE_
------------- ---------- ---------- ----------- --------- ---------
110 AC_ACCOUNT 1 8300 07-JUN-94 07-JUN-94
90 AD_VP 2 34000 13-JAN-93 21-SEP-89
50 ST_CLERK 20 55700 08-MAR-00 14-JUL-95
80 SA_REP 29 243500 21-APR-00 30-JAN-96
50 ST_MAN 5 36400 16-NOV-99 01-MAY-95
80 SA_MAN 5 61000 29-JAN-00 01-OCT-96
110 AC_MGR 1 12000 07-JUN-94 07-JUN-94
90 AD_PRES 1 24000 17-JUN-87 17-JUN-87
60 IT_PROG 5 28800 07-FEB-99 03-JAN-90
100 FI_MGR 1 12000 17-AUG-94 17-AUG-94
30 PU_CLERK 5 13900 10-AUG-99 18-MAY-95
50 SH_CLERK 20 64300 03-FEB-00 27-JAN-96
20 MK_MAN 1 13000 17-FEB-96 17-FEB-96
100 FI_ACCOUNT 5 39600 07-DEC-99 16-AUG-94
SA_REP 1 7000 24-MAY-99 24-MAY-99
70 PR_REP 1 10000 07-JUN-94 07-JUN-94
30 PU_MAN 1 11000 07-DEC-94 07-DEC-94
10 AD_ASST 1 4400 17-SEP-87 17-SEP-87
20 MK_REP 1 6000 17-AUG-97 17-AUG-97
40 HR_REP 1 6500 07-JUN-94 07-JUN-94

20 rows selected.
That's nice but what we really want is to see sub-totals at each of the levels (job and department), as well as a grand total for everything. Enter GROUPING SETS. With a very small syntax change, you can get those totals.


An Expert's Guide to Oracle Technology
by LewisC (Data Architect)
Lewis Cunningham is an Oracle ACE, Database Architect and self-professed database geek. Lewis has almost 20 years of database ...more
Blog Main / Archive / Invite Peers RSS for Posts / RSS for Comments
Previous Entry / Next Entry
Group By Grouping Sets
LewisC (Data Architect) posted 2/9/2009 | Comments (0)
Posted by the Database-Geek.

Summary Totals Using SQL

Have you ever needed to get totals for different combinations of columns in the same query? Did you ever want to create a nice little report from a simple SQL statement? Here's a tip that can make your life a lot easier when you need it. The great thing about this is that you can use it in a ref cursor called from other programming languages and reduce the number of calls you need to make to the database.

Let's say you are running queries against the Employees table of the sample HR schema provided by Oracle.

SQL> SELECT count(*) FROM Employees;

COUNT(*)
----------
107
Now, we want to get a total of salaries for each job in each department. We also want the max and min hire dates. We can do this with a simple query:

SELECT department_id,
job_id,
count(*),
sum(salary),
max(hire_date),
min(hire_date)
FROM Employees
GROUP BY department_id, job_id

/
DEPARTMENT_ID JOB_ID COUNT(*) SUM(SALARY) MAX(HIRE_ MIN(HIRE_
------------- ---------- ---------- ----------- --------- ---------
110 AC_ACCOUNT 1 8300 07-JUN-94 07-JUN-94
90 AD_VP 2 34000 13-JAN-93 21-SEP-89
50 ST_CLERK 20 55700 08-MAR-00 14-JUL-95
80 SA_REP 29 243500 21-APR-00 30-JAN-96
50 ST_MAN 5 36400 16-NOV-99 01-MAY-95
80 SA_MAN 5 61000 29-JAN-00 01-OCT-96
110 AC_MGR 1 12000 07-JUN-94 07-JUN-94
90 AD_PRES 1 24000 17-JUN-87 17-JUN-87
60 IT_PROG 5 28800 07-FEB-99 03-JAN-90
100 FI_MGR 1 12000 17-AUG-94 17-AUG-94
30 PU_CLERK 5 13900 10-AUG-99 18-MAY-95
50 SH_CLERK 20 64300 03-FEB-00 27-JAN-96
20 MK_MAN 1 13000 17-FEB-96 17-FEB-96
100 FI_ACCOUNT 5 39600 07-DEC-99 16-AUG-94
SA_REP 1 7000 24-MAY-99 24-MAY-99
70 PR_REP 1 10000 07-JUN-94 07-JUN-94
30 PU_MAN 1 11000 07-DEC-94 07-DEC-94
10 AD_ASST 1 4400 17-SEP-87 17-SEP-87
20 MK_REP 1 6000 17-AUG-97 17-AUG-97
40 HR_REP 1 6500 07-JUN-94 07-JUN-94

20 rows selected.
That's nice but what we really want is to see sub-totals at each of the levels (job and department), as well as a grand total for everything. Enter GROUPING SETS. With a very small syntax change, you can get those totals.

SELECT department_id,
job_id,
count(*),
sum(salary),
max(hire_date),
min(hire_date)
FROM Employees
GROUP BY GROUPING SETS (
(department_id, job_id),
(department_id),
())

/
DEPARTMENT_ID JOB_ID COUNT(*) SUM(SALARY) MAX(HIRE_ MIN(HIRE_
------------- ---------- ---------- ----------- --------- ---------
SA_REP 1 7000 24-MAY-99 24-MAY-99
1 7000 24-MAY-99 24-MAY-99
10 AD_ASST 1 4400 17-SEP-87 17-SEP-87
10 1 4400 17-SEP-87 17-SEP-87
20 MK_MAN 1 13000 17-FEB-96 17-FEB-96
20 MK_REP 1 6000 17-AUG-97 17-AUG-97
20 2 19000 17-AUG-97 17-FEB-96
30 PU_MAN 1 11000 07-DEC-94 07-DEC-94
30 PU_CLERK 5 13900 10-AUG-99 18-MAY-95
30 6 24900 10-AUG-99 07-DEC-94
40 HR_REP 1 6500 07-JUN-94 07-JUN-94
40 1 6500 07-JUN-94 07-JUN-94
50 ST_MAN 5 36400 16-NOV-99 01-MAY-95
50 SH_CLERK 20 64300 03-FEB-00 27-JAN-96
50 ST_CLERK 20 55700 08-MAR-00 14-JUL-95
50 45 156400 08-MAR-00 01-MAY-95
60 IT_PROG 5 28800 07-FEB-99 03-JAN-90
60 5 28800 07-FEB-99 03-JAN-90
70 PR_REP 1 10000 07-JUN-94 07-JUN-94
70 1 10000 07-JUN-94 07-JUN-94
80 SA_MAN 5 61000 29-JAN-00 01-OCT-96
80 SA_REP 29 243500 21-APR-00 30-JAN-96
80 34 304500 21-APR-00 30-JAN-96
90 AD_VP 2 34000 13-JAN-93 21-SEP-89
90 AD_PRES 1 24000 17-JUN-87 17-JUN-87
90 3 58000 13-JAN-93 17-JUN-87
100 FI_MGR 1 12000 17-AUG-94 17-AUG-94
100 FI_ACCOUNT 5 39600 07-DEC-99 16-AUG-94
100 6 51600 07-DEC-99 16-AUG-94
110 AC_MGR 1 12000 07-JUN-94 07-JUN-94
110 AC_ACCOUNT 1 8300 07-JUN-94 07-JUN-94
110 2 20300 07-JUN-94 07-JUN-94
107 691400 21-APR-00 17-JUN-87

33 rows selected.
Notice the change to the GROUP BY. I added a GROUPING SETS and then 3 columns lists.

(department_id, job_id) - Totals for the jobs within a department (listing each job_id)
(department_id) - Totals for the department over all (job not listed)
() - a grand total at the bottom
Play with this query a bit and try different combinations of GROUPING SETS. Add a new set, (job_id) and see what happens.

 

### SQL 中 `GROUPING SETS` 的使用方法 #### 语法 `GROUPING SETS` 是 SQL 标准的一部分,用于生成多维分组(Multi-Dimensional Grouping),允许在单个查询中同时对多个分组组合进行聚合[^5]。其基本语法如下: ```sql SELECT column1, column2, ..., aggregate_function(column3) FROM table_name WHERE condition GROUP BY GROUPING SETS ( (column1), (column2), (column1, column2), () ); ``` - `column1`, `column2`: 用于分组的列。 - `aggregate_function(column3)`: 对分组后的数据应用的聚合函数,例如 `SUM()`, `COUNT()`, `AVG()` 等。 - `GROUPING SETS`: 指定多个分组组合。 #### 示例:按地区和类别汇总销售额 假设有一个名为 `sales` 的表,包含以下字段:`region`(地区)、`category`(类别)、`sales_amount`(销售额)。可以使用以下 SQL 查询按不同维度汇总销售额: ```sql SELECT region, category, SUM(sales_amount) AS total_sales FROM sales GROUP BY GROUPING SETS ( (region), -- 按地区汇总 (category), -- 按类别汇总 (region, category) -- 按地区和类别汇总 ); ``` 此查询将返回三个层次的汇总结果:按地区、按类别以及按地区和类别的组合汇总销售额[^6]。 #### 包含总计行 可以通过在 `GROUPING SETS` 中添加空元组 `()` 来生成总计行: ```sql SELECT region, category, SUM(sales_amount) AS total_sales FROM sales GROUP BY GROUPING SETS ( (region, category), -- 按地区和类别汇总 () -- 总计行 ); ``` 此查询将在结果中包含一条总计行,显示所有记录的总销售额。 #### 使用 `GROUPING` 函数区分 NULL 当使用 `GROUPING SETS` 时,可能会出现多个维度的 `NULL` 值。为了区分这些 `NULL`,可以使用 `GROUPING` 函数。`GROUPING` 函数返回 1 表示该列为分组键的一部分,返回 0 表示该列为实际值[^7]。 ```sql SELECT CASE WHEN GROUPING(region) = 1 THEN 'All Regions' ELSE region END AS region, CASE WHEN GROUPING(category) = 1 THEN 'All Categories' ELSE category END AS category, SUM(sales_amount) AS total_sales FROM sales GROUP BY GROUPING SETS ( (region, category), (region), (category), () ); ``` 此查询将为每个分组组合生成清晰的标签,避免混淆。 #### 性能优化 `GROUPING SETS` 的性能通常优于多次独立查询的并集(`UNION ALL`),因为它可以在单次扫描中完成所有分组计算[^8]。然而,在大规模数据集上使用时,仍需注意索引和统计信息的优化。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值