0. 数据源
# student.sql
/*
Navicat Premium Data Transfer
Source Server : localhost_3306
Source Server Type : MySQL
Source Server Version : 80016
Source Host : localhost:3306
Source Schema : testdb
Target Server Type : MySQL
Target Server Version : 80016
File Encoding : 65001
Date: 03/05/2023 01:01:14
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`num` int(11) NOT NULL COMMENT '学号',
`name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'zhang' COMMENT '姓名',
`score` float NULL DEFAULT NULL,
`sex` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`addr` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
UNIQUE INDEX `num`(`num`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 109 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (101, 'zhang', 98, '男', '苏州');
INSERT INTO `student` VALUES (102, 'han', 69, '女', '西安');
INSERT INTO `student` VALUES (103, 'hui', 72, '女', '苏州');
INSERT INTO `student` VALUES (104, 'fang', 100, '男', '苏州');
INSERT INTO `student` VALUES (105, 'li', 88, '女', '北京');
INSERT INTO `student` VALUES (106, 'cheng', NULL, '女', '北京');
INSERT INTO `student` VALUES (107, 'zhao', NULL, '中', '北京');
SET FOREIGN_KEY_CHECKS = 1;
mysql> select * from student;
+-----+-------+-------+------+------+
| num | name | score | sex | addr |
+-----+-------+-------+------+------+
| 101 | zhang | 98 | 男 | 苏州 |
| 102 | han | 69 | 女 | 西安 |
| 103 | hui | 72 | 女 | 苏州 |
| 104 | fang | 100 | 男 | 苏州 |
| 105 | li | 88 | 女 | 北京 |
| 106 | cheng | NULL | 女 | 北京 |
| 107 | zhao | NULL | 中 | 北京 |
+-----+-------+-------+------+------+
7 rows in set (0.00 sec)
1. 分组查询
分组查询是对数据按照某个或多个字段进行分组
,MySQL中采用GROUP BY
关键字对数据进行分组。
格式:SELECT 字段列表 FROM 表名 [WHERE 条件] GROUP BY 分组字段名 [HAVING 条件表达式]
1.1 创建分组
// 根据性别进行分组
mysql> SELECT sex, COUNT(*) as count FROM student GROUP BY sex;
+------+-------+
| sex | count |
+------+-------+
| 男 | 2 |
| 女 | 4 |
| 中 | 1 |
+------+-------+
3 rows in set (0.00 sec)
// 如果要查看对应性别下的人名, 该如何???
// MySQL可以在GROUP BY字节中使用 GROUP_CONCAT()函数, 将每个分组中各个字段的值显示出来.
mysql> SELECT sex, GROUP_CONCAT(name) as names, COUNT(*) as count FROM student GROUP BY sex;
+------+------------------+-------+
| sex | names | count |
+------+------------------+-------+
| 中 | zhao | 1 |
| 女 | han,hui,li,cheng | 4 |
| 男 | zhang,fang | 2 |
+------+------------------+-------+
3 rows in set (0.00 sec)
注意:分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段毫无意义。
MYSQL8.0中测试发现,如果进行了分组,同时在SELECT后出现了分组字段之外的其他字段,那么一律执行报错。
// 按sex进行分组, 但是SELECT后却出现了分组字段sex之外的字段name, 执行sql语句时直接报错.
mysql> SELECT name, sex, COUNT(*) as count FROM student GROUP BY sex ;
ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'testdb.student.name' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
1.2 使用HAVING过滤分组
GROUP BY 可以和HAVING
一起限定显示记录所需满足的条件,只有满足条件的分组才会被显示
。
// 根据性别进行分组, 并且只有性别人数>=2的分组才显示
mysql> SELECT sex, GROUP_CONCAT(name) as names, COUNT(*) as count FROM student GROUP BY sex HAVING COUNT(*)>=2;
+------+------------------+-------+
| sex | names | count |
+------+------------------+-------+
| 女 | han,hui,li,cheng | 4 |
| 男 | zhang,fang | 2 |
+------+------------------+-------+
2 rows in set (0.00 sec)
// demo
// 查询num>=104的人, 并根据地址进行分组, 获取学生数量>=3的学生地址
mysql> SELECT addr, COUNT(*) as count FROM student WHERE num >= 104 GROUP BY addr HAVING COUNT(*)>=3;
+------+-------+
| addr | count |
+------+-------+
| 北京 | 3 |
+------+-------+
1 row in set (0.00 sec)
1.4 在GROUP BY子句中使用 WITH ROLLUP
使用WITH ROLLUP
关键字之后,在所有查询出的分组记录之后增加一条记录,该记录计算查询出的所有记录的总和,即统计记录数量
。
mysql> SELECT sex, COUNT(*) as count FROM student GROUP BY sex WITH ROLLUP;
+------+-------+
| sex | count |
+------+-------+
| 中 | 1 |
| 女 | 4 |
| 男 | 2 |
| NULL | 7 |
+------+-------+
4 rows in set (0.00 sec)
1.5 HAVING和WHERE的区别
HAVING和WHERE都是用来过滤数据,两者有何种区别呢???
其中重要的一点是,HAVING在数据分组之后进行排序过滤来选择分组;
而WHERE在分组之前用来过滤记录。
简而言之,就是WHERE在分组执行之前用来过滤记录的
;
HAVING则是分组执行之后用来过滤分组的
。