分组查询(group by、having)

分组查询

语法:

SELECT column, group_function,... FROM table
[WHERE condition]
GROUP BY group_by_expression
[HAVING group_condition];

说明

group_function:聚合函数

group_by_expression:分组表达式,多个之间用逗号隔开

group_condition:分组之后对数据进行过滤

分组中,select后面只能有两种类型的列:

  1. 出现在group by后的列
  2. 或者使用聚合函数的列

聚合函数

分组时可以使用聚合函数

函数 作用
max 查询指定列的最大值
min 查询指定列的最小值
count 统计查询结果的行数
sum 求和,返回指定列的总和
agv 求平均值,返回指定列数据的平均值

准备测试数据

mysql> drop table if exists t_order;

mysql> create table t_order( 
    id int not null AUTO_INCREMENT COMMENT '订单id', 
    user_id bigint not null comment '下单人id', 
    user_name varchar(16) not null default '' comment '用户名', 
    price decimal(10,2) not null default 0 comment '订单金额', 
    the_year SMALLINT not null comment '订单创建年份', 
    PRIMARY KEY (id) 
) comment '订单表';

mysql> insert into t_order(user_id,user_name,price,the_year) values (1001,'周星驰',11.11,'2017'), (1001,'周星驰',22.22,'2018'), (1001,'周星驰',88.88,'2018'), (1002,'刘德华',33.33,'2018'), (1002,'刘德华',12.22,'2018'), (1002,'刘德华',16.66,'2018'), (1002,'刘德华',44.44,'2019'), (1003,'张学友',55.55,'2018'), (1003,'张学友',66.66,'2019');

mysql> select * from t_order;
+----+---------+-----------+-------+----------+
| id | user_id | user_name | price | the_year |
+----+---------+-----------+-------+----------+
|  1 |    1001 | 周星驰    | 11.11 |     2017 |
|  2 |    1001 | 周星驰    | 22.22 |     2018 |
|  3 |    1001 | 周星驰    | 88.88 |     2018 |
|  4 |    1002 | 刘德华    | 33.33 |     2018 |
|  5 |    1002 | 刘德华    | 12.22 |     2018 |
|  6 |    1002 | 刘德华    | 16.66 |     2018 |
|  7 |    1002 | 刘德华    | 44.44 |     2019 |
|  8 |    1003 | 张学友    | 55.55 |     2018 |
|  9 |    1003 | 张学友    | 66.66 |     2019 |
+----+---------+-----------+-------+----------+
9 rows in set (0.00 sec)

单字段分组

需求:查询每个用户下单数量,输出:用户id、下单数量,如下:

mysql> select user_id 用户id, COUNT(id) 下单数量 from t_order group by user_id;
+----------+--------------+
| 用户id   | 下单数量     |
+----------+--------------+
|     1001 |            3 |
|     1002 |            4 |
|     1003 |            2 |
+----------+--------------+
3 rows in set (0.00 sec)

多字段分组

需求:查询每个用户每年下单数量,输出字段:用户id、年份、下单数量,如下:

mysql>  SELECT user_id 用户id, the_year 年份, COUNT(id) 下单数量 FROM t_order GROUP BY user_id , the_year;
+----------+--------+--------------+
| 用户id   | 年份   | 下单数量     |
+----------+--------+--------------+
|     1001 |   2017 |            1 |
|     1001 |   2018 |            2 |
|     1002 |   2018 |            3 |
|     1002 |   2019 |            1 |
|     1003 |   2018 |            1 |
|     1003 |   2019 |            1 |
+----------+--------+--------------+
6 rows in set (0.00 sec)

分组前筛选数据

分组前对数据进行筛选,使用where关键字

需求:需要查询2018年每个用户下单数量,输出:用户id、下单数量,如下:

SELECT
    user_id 用户id, COUNT(id) 下单数量 
FROM
    t_order t 
WHERE
    t.the_year = 2018 
GROUP BY user_id;

+----------+--------------+
| 用户id   | 下单数量     |
+----------+--------------+
|     1001 |            2 |
|     1002 |            3 |
|     1003 |            1 |
+----------+--------------+
3 rows in set (0.01 sec)

分组后对数据筛选,使用having关键字

需求:查询2018年订单数量大于1的用户,输出:用户id,下单数量,如下:

分组后筛选数据(having)

分组后对数据筛选,使用having关键字

作用:having 子句的作用是筛选满足条件的组,即在分组之后过滤数据,条件中经常包含聚组函数,使用having 条件显示特定的组,也可以使用多个分组标准进行分组。

语法:select 列名 , 列函数 from 表名 where 条件 group by 列名 having 子句;

需求:查询2018年订单数量大于1的用户,输出:用户id,下单数量,如下:

方式1:

SELECT
    user_id 用户id, COUNT(id) 下单数量 
FROM
    t_order t
WHERE
    t.the_year = 2018
GROUP BY user_id
HAVING count(id)>=2;

+----------+--------------+
| 用户id   | 下单数量     |
+----------+--------------+
|     1001 |            2 |
|     1002 |            3 |
+----------+--------------+
2 rows in set (0.00 sec)

方式2:

SELECT
    user_id 用户id, COUNT(id) 下单数量 
FROM
    t_order t
WHERE
    t.the_year = 2018
GROUP BY user_id
HAVING 下单数量>=2;

+----------+--------------+
| 用户id   | 下单数量     |
+----------+--------------+
|     1001 |            2 |
|     1002 |            3 |
+----------+--------------+
2 rows in set (0.00 sec)

where和having的区别

where是在分组(聚合)前对记录进行筛选,而having是在分组结束后的结果里筛选,最后返回整个sql的查询结果。

可以把having理解为两级查询,即含having的查询操作先获得不含having子句时的sql查询结果表,然后在这个结果表上使用having条件筛选出符合的记录,最后返回这些记录,因此,having后是可以跟聚合函数的,并且这个聚集函数不必与select后面的聚集函数相同。

分组后排序

需求:获取每个用户最大金额,然后按照最大金额倒序,输出:用户id,最大金额,如下:

mysql> select user_id 用户id, max(price) 最大金额 from t_order group by user_id;
+----------+--------------+
| 用户id   | 最大金额     |
+----------+--------------+
|     1001 |        88.88 |
|     1002 |        44.44 |
|     1003 |        66.66 |
+----------+--------------+
3 rows in set (0.00 sec)

mysql> select user_id 用户id, max(price) 最大金额 from t_order group by user_id order by max(price) desc;
+----------+--------------+
| 用户id   | 最大金额     |
+----------+--------------+
|     1001 |        88.88 |
|     1003 |        66.66 |
|     1002 |        44.44 |
+----------+--------------+
3 rows in set (0.00 sec)

where & group by & having & order by & limit 一起使用

where、group by、having、order by、limit这些关键字一起使用时,先后顺序有明确的限制,语法如

下:

select 列 from 
表名
where [查询条件] 
group by [分组表达式] 
having [分组过滤条件] 
order by [排序条件] 
limit [offset,] count;

注意:

写法上面必须按照上面的顺序来写。

需求:查询出2018年,下单数量大于等于2的,按照下单数量降序排序,最后只输出第1条记录,显

示:用户id,下单数量,如下:

SELECT
    user_id 用户id, COUNT(id) 下单数量
FROM
    t_order t
WHERE
    t.the_year = 2018
GROUP BY user_id
HAVING count(id)>=2
ORDER BY 下单数量 DESC;
+----------+--------------+
| 用户id   | 下单数量     |
+----------+--------------+
|     1002 |            3 |
|     1001 |            2 |
+----------+--------------+
2 rows in set (0.00 sec)



SELECT
    user_id 用户id, COUNT(id) 下单数量
FROM
    t_order t
WHERE
    t.the_year = 2018
GROUP BY user_id
HAVING count(id)>=2
ORDER BY 下单数量 DESC
LIMIT 1;
+----------+--------------+
| 用户id   | 下单数量     |
+----------+--------------+
|     1002 |            3 |
+----------+--------------+
1 row in set (0.00 sec)

in多列查询

需求:获取每个用户下单的最大金额及下单的年份,输出:用户id,最大金额,年份

SELECT
    user_id 用户id, price 最大金额, the_year 年份
FROM
    t_order t1
WHERE
    (t1.user_id , t1.price)
    IN
    (SELECT t.user_id, MAX(t.price) FROM t_order t GROUP BY t.user_id);
+----------+--------------+--------+
| 用户id   | 最大金额     | 年份   |
+----------+--------------+--------+
|     1001 |        88.88 |   2018 |
|     1002 |        44.44 |   2019 |
|     1003 |        66.66 |   2019 |
+----------+--------------+--------+
3 rows in set (0.00 sec)


#分解一下
where之前
mysql> SELECT
    ->     user_id 用户id, price 最大金额, the_year 年份
    -> FROM
    ->     t_order t1;
+----------+--------------+--------+
| 用户id   | 最大金额     | 年份   |
+----------+--------------+--------+
|     1001 |        11.11 |   2017 |
|     1001 |        22.22 |   2018 |
|     1001 |        88.88 |   2018 |
|     1002 |        33.33 |   2018 |
|     1002 |        12.22 |   2018 |
|     1002 |        16.66 |   2018 |
|     1002 |        44.44 |   2019 |
|     1003 |        55.55 |   2018 |
|     1003 |        66.66 |   2019 |
+----------+--------------+--------+
9 rows in set (0.00 sec)
in之后的
mysql> SELECT t.user_id, MAX(t.price) FROM t_order t GROUP BY t.user_id
    -> ;
+---------+--------------+
| user_id | MAX(t.price) |
+---------+--------------+
|    1001 |        88.88 |
|    1002 |        44.44 |
|    1003 |        66.66 |
+---------+--------------+
3 rows in set (0.00 sec)

总结:

分组中select后面的列只能有2种:

  • 出现在group by后面的列

  • 使用聚合函数的列

oracle、sqlserver、db2中也是按照这种规范来的。

文中使用的是5.7版本,默认是按照这种规范来的。

posted @ 2021-02-07 15:49  EverEternity  阅读(343)  评论(0编辑  收藏  举报