group by本质论与groupby多个字段

一、group by本质论

转载自:https://leetcode-cn.com/problems/reformat-department-table/solution/group-byben-zhi-lun-by-loverxp-7mgy/

1179. 重新格式化部门表

SQL架构

部门表 Department

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| id            | int     |
| revenue       | int     |
| month         | varchar |
+---------------+---------+
(id, month) 是表的联合主键。
这个表格有关于每个部门每月收入的信息。
月份(month)可以取下列值 ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]。

 

编写一个 SQL 查询来重新格式化表,使得新的表中有一个部门 id 列和一些对应 每个月 的收入(revenue)列。

查询结果格式如下面的示例所示:

Department 表:
+------+---------+-------+
| id   | revenue | month |
+------+---------+-------+
| 1    | 8000    | Jan   |
| 2    | 9000    | Jan   |
| 3    | 10000   | Feb   |
| 1    | 7000    | Feb   |
| 1    | 6000    | Mar   |
+------+---------+-------+

查询得到的结果表:
+------+-------------+-------------+-------------+-----+-------------+
| id   | Jan_Revenue | Feb_Revenue | Mar_Revenue | ... | Dec_Revenue |
+------+-------------+-------------+-------------+-----+-------------+
| 1    | 8000        | 7000        | 6000        | ... | null        |
| 2    | 9000        | null        | null        | ... | null        |
| 3    | null        | 10000       | null        | ... | null        |
+------+-------------+-------------+-------------+-----+-------------+

注意,结果表有 13 列 (1个部门 id 列 + 12个月份的收入列)。

解题思路

发现很多童鞋不理解group by,以及为什么要使用sum或max,这里深入讲解一下。

对group by理解不透彻有可能是MySQL用户专有的问题,因为在MySQL中,允许下面这样的写法

select id, revenue
from Department
group by id

即在select子句中出现了group by子句中没有出现的列名revenue,而这种写法在SQL标准中是没有的,在MySQL以外的大部分数据库中也是不支持的,因为逻辑上没有意义。

要理解为什么这种写法没有意义,我们来看一看题目本身提供的样例数据。(注意,其中的id并不是真正的id,只是部门编号)

+------+---------+-------+
| id   | revenue | month |
+------+---------+-------+
| 1    | 8000    | Jan   |
| 2    | 9000    | Jan   |
| 3    | 10000   | Feb   |
| 1    | 7000    | Feb   |
| 1    | 6000    | Mar   |
+------+---------+-------+

通过group by id,该表中的数据被分为三组,即

+------+---------+-------+
| 1    | 8000    | Jan   |
| 1    | 7000    | Feb   |
| 1    | 6000    | Mar   |
+------+---------+-------+
+------+---------+-------+
| 2    | 9000    | Jan   |
+------+---------+-------+
+------+---------+-------+
| 3    | 10000   | Feb   |
+------+---------+-------+

此时在第一组中,有三条记录,也就是说有三个revenue,那么此时select id, revenue就无法判定应该取哪一个revenue,所以这样的操作在标准SQL中是不允许的,只能通过聚合函数来处理。而MySQL在这里提供了一种便利的方式,却让理解它的工作方式变得更加困难。

聚合函数本质上就是常见编程语言里的reduce,也就是把一组数据处理成一个单一数据,所以可以这样写:

select id, sum(revenue) as total
from Department
group by id

来表示一个部门的总收入。

这个操作在javascript中类似于这样:

departments.reduce(total, department => total + department.revenue)

max等其它聚合函数同理,都可以找到用reduce表示的等价形式。

那么代码

sum(case month when 'Jan' then revenue end) as Jan_Revenue,

的意思就是说把所有的revenue聚合处理,处理的方法是,如果month的值是Jan,那么结果就是revenue,否则忽略。
 

select id,
    sum(case month when 'Jan' then revenue end) as Jan_Revenue,
    sum(case month when 'Feb' then revenue end) as Feb_Revenue,
    sum(case month when 'Mar' then revenue end) as Mar_Revenue,
    sum(case month when 'Apr' then revenue end) as Apr_Revenue,
    sum(case month when 'May' then revenue end) as May_Revenue,
    sum(case month when 'Jun' then revenue end) as Jun_Revenue,
    sum(case month when 'Jul' then revenue end) as Jul_Revenue,
    sum(case month when 'Aug' then revenue end) as Aug_Revenue,
    sum(case month when 'Sep' then revenue end) as Sep_Revenue,
    sum(case month when 'Oct' then revenue end) as Oct_Revenue,
    sum(case month when 'Nov' then revenue end) as Nov_Revenue,
    sum(case month when 'Dec' then revenue end) as Dec_Revenue
from Department
group by id

二、groupby多个字段

案例:

在平时的开发任务中我们经常会用到MYSQL的GROUP BY分组, 用来获取数据表中以分组字段为依据的统计数据。比如有一个学生选课表,表结构如下:

Table: Subject_Selection

Subject   Semester   Attendee
---------------------------------
ITB001    1          John
ITB001    1          Bob
ITB001    1          Mickey
ITB001    2          Jenny
ITB001    2          James
MKB114    1          John
MKB114    1          Erica

我们想统计每门课程有多少个学生报名,应用如下SQL:

SELECT Subject, Count(*)
FROM Subject_Selection
GROUP BY Subject

得到如下结果:

Subject    Count
------------------------------
ITB001     5
MKB114     2

因为表里记录了有5个学生选择ITB001,2个学生选择了MKB114。

产生这个结果的原因是:
GROUP BY X意思是将所有具有相同X字段值的记录放到一个分组里。
那么GROUP BY X, Y呢?
GROUP BY X, Y意思是将所有具有相同X字段值和Y字段值的记录放到一个分组里。

我们下面再接着要求统计出每门学科每个学期有多少人选择,应用如下SQL:

SELECT Subject, Semester, Count(*)
FROM Subject_Selection
GROUP BY Subject, Semester

上面SQL的意思是,对Subject_Selection表中的数据进行分组,将具有相同Subject和Semester字段值的记录放到同一个分组里去, 然后对每个分组中的数据应用聚合函数(COUNT,SUM, AVG,etc)。

得到的结果是:

Subject    Semester   Count
------------------------------
ITB001     1          3
ITB001     2          2
MKB114     1          2

从表中的记录我们可以看出这个分组结果是正确的有3个学生在第一学期选择了ITB001, 2个学生在第二学期选择了ITB001,
还有两个学生在第一学期选择了MKB114, 没人在第二学期选择MKB114。

再比如有一个订单表,表中记录了所有支付过的订单

Table: Order

Product   Buyer       Spending
---------------------------------
PD001     Todd          12.00
PD001     Todd          12.00
PD001     Todd          12.00
PD001     Lily          12.00
PD001     Lily          12.00
PD002     Todd          20.00
PD002     Todd          20.00

现在我们想统计每个用户在每种商品上总共花了多少钱,执行如下SQL

SELECT Product,Buyer, SUM(Spending)
FROM `Order`
GROUP BY  Buyer,Product

获得的结果如下:

总结:

在MYSQL中使用GROUP BY对表中的数据进行分组时,
GROUP BY X意思是将所有具有相同X字段值的记录放到一个分组里,
GROUP BY X, Y意思是将所有具有相同X字段值和Y字段值的记录放到一个分组里。

 

<think>好的,用户想了解SQL中的GROUP BY使用方法,并提供示例。我需要先回顾GROUP BY的基本概念和常见用法。 首先,GROUP BY的作用是对数据进行分组,通常聚合函数一起使用,比如COUNT、SUM、AVG等。需要解释清楚GROUP BY子句的位置,跟在WHERE之后,在HAVING之前。同时,要说明分组后的筛选必须用HAVING而不是WHERE。 接下来,应该给出基本语法结构,可能用代码块展示示例。例如,SELECT department, COUNT(*) FROM employees GROUP BY department。这样用户能直观看到结构。 然后,常见使用场景可能需要分点说明,比如统计各部门人数、计算平均工资等。每个场景配一个SQL示例,这样用户更容易理解。 需要注意GROUP BY的常见错误,比如SELECT中的非聚合列未包含在GROUP BY中,这会引发错误。例如,如果选了员工姓名但没在GROUP BY里,就会报错。这时候需要提醒用户注意列的选择。 还要提到HAVING的使用,和WHERE的区别。比如HAVING用于过滤分组后的结果,而WHERE是在分组前过滤数据。举个例子,筛选平均工资大于5000的部门,需要用HAVING AVG(salary) > 5000。 多列分组也是一个重要点,比如按部门和职位分组统计人数。示例可以展示GROUP BY department, job_title。 最后,给出一个完整的示例,比如创建示例表,插入数据,然后执行包含GROUP BY的查询,并展示结果。这能帮助用户实际看到效果。 可能还需要生成相关问题,比如优化GROUP BY查询的方法,窗口函数的区别,性能影响等。这些问题有助于用户进一步学习。 需要确保所有数学表达式用正确的LaTeX格式,但在这个主题下可能涉及较少数学内容,但如果有比如计算平均值等,可能需要用$AVG(salary)$这样的表示。不过通常直接写函数名即可。 检查是否有需要引用文献的地方,比如如果有提到特定数据库的优化方法,可能需要添加引用,但一般基础GROUP BY可能不需要。不过根据用户给的示例,引用可能存在,但用户这里没有提供相关引用源,所以可能不需要添加。 最后,保持回答结构清晰,分点说明,代码示例正确,并注意常见错误和注意事项,确保用户能顺利理解并应用GROUP BY。</think># SQL GROUP BY 使用详解 ### 一、基本作用 `GROUP BY` 用于将数据按指定列分组,**配合聚合函数**实现分组统计,例如: $$ \text{SELECT 分组列, COUNT(*)/SUM(数值列) FROM 表 GROUP BY 分组列} $$ ### 二、基础语法 ```sql SELECT column1, aggregate_function(column2) FROM table WHERE condition GROUP BY column1 HAVING aggregate_condition ``` ### 三、典型应用场景 1. **统计部门人数** ```sql SELECT department, COUNT(*) AS employee_count FROM employees GROUP BY department ``` 2. **计算平均工资** ```sql SELECT job_title, AVG(salary) AS avg_salary FROM employees WHERE hire_date > '2020-01-01' GROUP BY job_title ``` 3. **多列分组统计** ```sql SELECT department, job_level, MAX(salary) AS top_salary FROM employees GROUP BY department, job_level ``` ### 四、注意事项 1. **SELECT子句限制**:非聚合列必须出现在GROUP BY中 ✅ 正确:`SELECT department, COUNT(*)` ❌ 错误:`SELECT name, department, COUNT(*)`(name未参分组) 2. **筛选条件顺序** - `WHERE`:分组前过滤原始数据 - `HAVING`:分组后过滤聚合结果 ```sql SELECT department, AVG(salary) FROM employees WHERE salary > 3000 GROUP BY department HAVING AVG(salary) > 5000 ``` ### 五、完整示例演示 ```sql -- 创建销售记录表 CREATE TABLE sales ( order_id INT PRIMARY KEY, product VARCHAR(50), category VARCHAR(20), amount DECIMAL(10,2), region VARCHAR(20) ); -- 插入示例数据 INSERT INTO sales VALUES (1, 'Keyboard', 'Electronics', 89.99, 'North'), (2, 'Mouse', 'Electronics', 49.99, 'South'), (3, 'Notebook', 'Stationery', 2.99, 'North'), (4, 'Monitor', 'Electronics', 199.99, 'North'); -- 按类别统计销售总额 SELECT category, SUM(amount) AS total_sales, COUNT(*) AS order_count FROM sales GROUP BY category ``` 执行结果: | category | total_sales | order_count | |--------------|-------------|-------------| | Electronics | 339.97 | 3 | | Stationery | 2.99 | 1 | ### 六、进阶技巧 1. **ROLLUP扩展**:生成分级小计 ```sql SELECT region, category, SUM(amount) FROM sales GROUP BY ROLLUP(region, category) ``` 2. **GROUPING SETS**:多维度组合统计 ```sql SELECT region, category, SUM(amount) FROM sales GROUP BY GROUPING SETS ((region), (category), ()) ```
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值