SQL分组过滤 - HAVING子句

阅读:474
上一节我们介绍了GROUP BY子句,可以对数据分组。那么本节讲介绍对GROUP BY分组数据如何过滤。

1. HAVING

当对分组的数据进行过滤时,使用HAVING子句。

现在我们准备一个表,课程表t_course,并插入一些数据,用于我们进行测试演示,只需要复制到MySQL客户端,回车执行即可。脚本如下:

建表SQL语句:

DROP TABLE IF EXISTS `t_course`;
CREATE TABLE `t_course` (
  `id` bigint(20) NOT NULL COMMENT '课程编号',
  `title` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '课程名称',
  `intro` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '课程介绍',
  `sell_price` decimal(18,2) DEFAULT NULL COMMENT '课程售价',
  `hot` int(11) DEFAULT NULL COMMENT '热度',
  `category_id` bigint(20) DEFAULT NULL COMMENT '课程分类',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

 

课程数据Insert语句:

INSERT INTO `t_course` VALUES (1, 'Java教程:核心技术一网打尽', 'Java教程:核心技术一网打尽', 99.00, 8766317, 1, '2020-12-09 11:27:50', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (2, 'Java面试教程', 'Java面试教程', 100.00, 76012, 1, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (3, 'redis基础教程', 'redis基础教程,该课程持续更新中...', 19.00, 1066, 3, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (4, '深入理解MySQL底层原理', '深入理解MySQL底层原理', 29.00, 1210, 3, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (5, 'kafka教程', 'kafka教程', 39.00, 382, 3, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (6, 'Spring Cloud基础教程', 'Spring Cloud基础教程', 40.00, 885, 1, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (7, '架构师教程', '架构师教程100,该课程持续更新中...', 66.00, 769, 2, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (8, 'git教程', 'git教程,该课程持续更新中...', 55.00, 761, 1, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (9, '深入剖析Java虚拟机', '深入剖析Java虚拟机', 199.00, 369, 1, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (10, 'SQL教程', 'SQL教程,该课程持续更新中...', 299.00, 179, 3, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (11, '设计模式:GOF的23种设计模式全剖析', '设计模式:GOF的23种设计模式全剖析。该课程持续更新中...', 9.00, 121, 1, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (12, 'HTML教程', 'HTML教程,该课程持续更新中...', 1.00, 122, 10, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (13, 'HTML5教程', 'HTML5教程,该课程持续更新中...', 6.00, 114, 10, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (14, 'CSS教程', 'CSS教程,该课程持续更新中...', 10.00, 120, 10, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (15, 'JavaScript教程', 'JavaScript教程,该课程持续更新中...', 15.00, 123, 10, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (16, 'HTTP协议', 'HTTP协议,该课程持续更新中...', 45.00, 120, 10, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (17, 'Tomcat教程', 'Tomcat教程,该课程持续更新中...', 10.00, 124, 3, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (18, 'Servlet教程', 'Servlet教程,该课程持续更新中...', 9.00, 121, 1, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (19, 'Jsp教程', 'Jsp教程,该课程持续更新中...', 3.00, 122, 10, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (20, 'Ajax教程', 'Ajax教程,该课程持续更新中...', 10.00, 123, 10, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (21, 'JQuery教程', 'JQuery教程,该课程持续更新中...', 29.00, 95, 10, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (22, 'Maven教程', 'Maven教程,该课程持续更新中...', 39.00, 121, 1, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (23, 'Linux教程', 'Linux教程,该课程持续更新中...', 59.00, 122, 1, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (24, 'JDBC教程', 'JDBC教程,该课程持续更新中...', 9.00, 125, 1, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (25, 'Spring教程', 'Spring教程,该课程持续更新中...', 10.00, 122, 1, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (26, 'Spring MVC教程', 'Spring MVC教程,该课程持续更新中...', 11.00, 124, 1, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (27, 'Spring boot教程', 'Spring boot教程,该课程持续更新中...', 13.00, 122, 1, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (28, 'MyBatis教程', 'MyBatis教程,该课程持续更新中...', 15.00, 126, 1, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (29, 'ActiveMQ教程', 'ActiveMQ教程,该课程持续更新中...', 12.00, 123, 3, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (30, 'ZooKeeper教程', 'ZooKeeper教程,该课程持续更新中...', 13.00, 120, 3, '2020-12-08 11:28:04', '2020-12-21 11:28:42');
INSERT INTO `t_course` VALUES (31, 'dubbo教程', 'dubbo教程,该课程持续更新中...', 58.00, 124, 3, '2020-12-08 11:28:04', '2020-12-21 11:28:42');

t_course,课程表,可以查看SQL注释了解下结构。

现在我们要对课程按照category_id(课程分类的ID)分组,也就是按照课程分类分组,并统计每个课程分类内课程总数,SQL如下:

SELECT category_id, COUNT(id) FROM t_course GROUP BY category_id;

 

执行结果:

+-------------+-----------+
| category_id | COUNT(id) |
+-------------+-----------+
|           1 |        14 |
|           2 |         1 |
|           3 |         8 |
|          10 |         8 |
+-------------+-----------+
4 rows in set (0.00 sec)

现在我们需要对分组数据进行过滤,去除分类内课程数小于2的分类,执行以下SQL:

SELECT category_id, COUNT(id) FROM t_course GROUP BY category_id HAVING COUNT(id) >2 ;

 

执行查看结果:

+-------------+-----------+
| category_id | COUNT(id) |
+-------------+-----------+
|           1 |        14 |
|           3 |         8 |
|          10 |         8 |
+-------------+-----------+
3 rows in set (0.00 sec)

 

通过以上例子,你会看到对分组后的数据进行过滤,使用HAVING子句。

 

2. HAVING和WHERE

细心的你,肯定发现,HAVING和WHERE很相似。不错,HAVING支持所有WHERE的操作符。包括通配符条件和多个操作符子句。

但是,HAVING和WHERE有很大的区别,用途不同。Where是用在数据分组前,对数据行进行过滤。HAVING是用在分组后,对分组数据进行过滤。

熟悉,HAVING和WHERE之间的区别后,我们举一个HAVING和WHERE混用的例子,对课程分类进行分组,查询出课程价格大于20,每个分类课程数大于2的分类,SQL如下:

SELECT category_id, COUNT(id) FROM t_course WHERE sell_price > 20 GROUP BY category_id HAVING COUNT(id) > 2 ;

 

结果如下:

+-------------+-----------+
| category_id | COUNT(id) |
+-------------+-----------+
|           1 |         7 |
|           3 |         4 |
+-------------+-----------+
2 rows in set (0.01 sec)

上面的例子,WHERE先过滤出所有课程价格大于20的所有课程,然后对课程按照分类进行分组,HAVING过滤数该分类课程数大于2的分类。

 

总结:

  • 对分组结果进行过滤使用HAVING子句。
  • WHERE支持的操作HAVING都支持。
  • HAVING是对分组数据进行过滤,WHERE是对分组前数据行进行过滤。

赞赏支持


精彩留言

发表评论