神奇的 SQL 之 HAVING → 容易被輕視的主角
閱讀本文大概需要 8 分鐘。
初識 HAVING
DROP TABLE IF EXISTS tbl_student_class;
CREATE TABLE tbl_student_class (
id int(8) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',
sno varchar(12) NOT NULL COMMENT '學號',
cno varchar(5) NOT NULL COMMENT '班級號',
cname varchar(50) NOT NULL COMMENT '班級名',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='學生班級表';
-- ----------------------------
-- Records of tbl_student_class
-- ----------------------------
INSERT INTO tbl_student_class(sno, cno, cname) VALUES ('20190607001', '0607', '影視7班');
INSERT INTO tbl_student_class(sno, cno, cname) VALUES ('20190607002', '0607', '影視7班');
INSERT INTO tbl_student_class(sno, cno, cname) VALUES ('20190608003', '0608', '影視8班');
INSERT INTO tbl_student_class(sno, cno, cname) VALUES ('20190608004', '0608', '影視8班');
INSERT INTO tbl_student_class(sno, cno, cname) VALUES ('20190609005', '0609', '影視9班');
INSERT INTO tbl_student_class(sno, cno, cname) VALUES ('20190609006', '0609', '影視9班');
INSERT INTO tbl_student_class(sno, cno, cname) VALUES ('20190609007', '0609', '影視9班');
SELECT cno, COUNT(*) nums FROM tbl_student_class GROUP BY cno HAVING COUNT(*) = 3;



HAVING 子句的構成要素
SELECT cno, COUNT(*) nums FROM tbl_student_class GROUP BY cno HAVING cname = '影視9班';
[Err] 1054 - Unknown column 'cname' in 'having clause'

HAVING 的魅力
是否存在缺失的編號
DELETE FROM tbl_student_class WHERE id IN(2,5,6);
SELECT * FROM tbl_student_class;

SELECT '存在缺失的編號' AS gap
FROM tbl_student_class
HAVING COUNT(*) <> MAX(id) - MIN(id) + 1;
-- 無論如何都有結(jié)果返回
SELECT CASE WHEN COUNT(*) = 0 THEN '表為空'
WHEN COUNT(*) <> MAX(id) - MIN(id) + 1 THEN '存在缺失的編號'
ELSE '連續(xù)' END AS gap
FROM tbl_student_class;
求眾數(shù)
DROP TABLE IF EXISTS tbl_student_salary;
CREATE TABLE tbl_student_salary (
id int(8) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',
name varchar(5) NOT NULL COMMENT '姓名',
salary DECIMAL(15,2) NOT NULL COMMENT '年薪, 單位元',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='畢業(yè)生年薪標';
insert into tbl_student_salary values (1,'李小龍', 1000000);
insert into tbl_student_salary values (2,'李四', 50000);
insert into tbl_student_salary values (3,'王五', 50000);
insert into tbl_student_salary values (4,'趙六', 50000);
insert into tbl_student_salary values (5,'張三', 70000);
insert into tbl_student_salary values (6,'張一三', 70000);
insert into tbl_student_salary values (7,'張二三', 70000);
insert into tbl_student_salary values (8,'張三三', 60000);
insert into tbl_student_salary values (9,'張三四', 40000);
insert into tbl_student_salary values (10,'張三豐', 30000);
-- 使用謂詞 ALL 求眾數(shù)
SELECT salary, COUNT(*) AS cnt
FROM tbl_student_salary
GROUP BY salary
HAVING COUNT(*) >= ALL (
SELECT COUNT(*)
FROM tbl_student_salary
GROUP BY salary);

-- 使用極值函數(shù)求眾數(shù)
SELECT salary, COUNT(*) AS cnt
FROM tbl_student_salary
GROUP BY salary
HAVING COUNT(*) >= (
SELECT MAX(cnt)
FROM (
SELECT COUNT(*) AS cnt
FROM tbl_student_salary
GROUP BY salary
) TMP
) ;
求中位數(shù)

-- 求中位數(shù)的SQL 語句:在HAVING 子句中使用非等值自連接
SELECT AVG(DISTINCT salary)
FROM (
SELECT T1.salary
FROM tbl_student_salary T1, tbl_student_salary T2
GROUP BY T1.salary
-- S1 的條件
HAVING SUM(CASE WHEN T2.salary >= T1.salary THEN 1 ELSE 0 END) >= COUNT(*) / 2
-- S2 的條件
AND SUM(CASE WHEN T2.salary <= T1.salary THEN 1 ELSE 0 END) >= COUNT(*) / 2
) TMP;
>= COUNT(*)/2 里的等號,加上等號并不是為了清晰地分開子集 S1 和 S2,而是為了讓這 2 個子集擁有共同部分> COUNT(*)/2 ,那么當元素個數(shù)為偶數(shù)時,S1 和 S2 就沒有共同的元素了,也就無法求出中位數(shù)了;加上等號是為了寫出通用性更高的 SQL查詢不包含 NULL 的集合
DROP TABLE IF EXISTS tbl_student_submit_log;
CREATE TABLE tbl_student_submit_log (
id int(8) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',
sno varchar(12) NOT NULL COMMENT '學號',
dept varchar(50) NOT NULL COMMENT '學院',
submit_date DATE COMMENT '提交日期',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='學生報告提交記錄表';
insert into tbl_student_submit_log values
(1,'20200607001', '理學院', '2020-12-12'),
(2,'20200607002', '理學院', '2020-12-13'),
(3,'20200608001', '文學院', null),
(4,'20200608002', '文學院', '2020-12-22'),
(5,'20200608003', '文學院', '2020-12-22'),
(6,'20200612001', '工學院', null),
(7,'20200617001', '經(jīng)濟學院', '2020-12-23');
SELECT dept
FROM tbl_student_submit_log
GROUP BY dept
HAVING COUNT(*) = COUNT(submit_date);
SELECT dept
FROM tbl_student_submit_log
GROUP BY dept
HAVING COUNT(*) = SUM(
CASE WHEN submit_date IS NOT NULL THEN 1
ELSE 0 END
);
其他
聚合鍵條件的歸屬


總結(jié)

推薦閱讀:
微信掃描二維碼,關注我的公眾號
朕已閱 

