第二节课程
Slide: https://15445.courses.cs.cmu.edu/fall2022/slides/02-modernsql.pdf Notes: https://15445.courses.cs.cmu.edu/fall2022/notes/02-modernsql.pdf
数据准备
PostgreSQL
CREATE TABLE student (
sid INT PRIMARY KEY,
name VARCHAR(16),
login VARCHAR(32) UNIQUE,
age SMALLINT,
gpa FLOAT
);
CREATE TABLE course (
cid VARCHAR(32) PRIMARY KEY,
name VARCHAR(32) NOT NULL
);
CREATE TABLE enrolled (
sid INT REFERENCES student (sid),
cid VARCHAR(32) REFERENCES course (cid),
grade CHAR(1)
);
INSERT INTO student (sid, name, login, age, gpa) VALUES
(53666, 'Kanye', 'kayne@cs', 39 ,4.0),
(53688, 'Bieber', 'jbieber@cs', 26, 3.9),
(53655, 'Tupac', 'shakur@cs', 26, 3.5);
INSERT INTO course (cid, name) VALUES
('15-445', 'Database Systems'),
('15-721', 'Advanced Database Systems'),
('15-826', 'Data Mining'),
('15-823', 'Advanced Topics in Databases');
INSERT INTO enrolled (sid, cid, grade) VALUES
(53666, '15-445', 'C'),
(53688, '15-721', 'A'),
(53688, '15-826', 'B'),
(53655, '15-445', 'B'),
(53666, '15-721', 'C');
Aggregate
有以下五种基本的聚合函数:
- COUNT
- SUM
- AVG
- MAX
- MIN
COUNT
PostgreSQL
-- 查询 student 表中的记录数
SELECT COUNT(*) FROM student;
-- 查询 cs 专业的学生人数, % 表示任意字符任意次数
SELECT COUNT(login) FROM student WHERE login LIKE '%@cs';
SELECT COUNT(*) FROM student WHERE login LIKE '%@cs';
SELECT COUNT(1) FROM student WHERE login LIKE '%@cs';
SELECT COUNT(1+1+1) FROM student WHERE login LIKE '%@cs';
COUNT() 函数的参数可以是任意的表达式,例如 数学表达式、字段名、常量等。看起来很奇怪。
多个聚合函数
PostgreSQL
SELECT AVG(gpa), COUNT(sid)
FROM student WHERE login LIKE '%@cs';
DISTINCT 去重
PostgreSQL
-- 拥有不重复 login 的 cs 学生人数
SELECT COUNT(DISTINCT login)
FROM student WHERE login LIKE '%@cs';
GROUP BY
PostgreSQL
SELECT AVG(s.gpa), e.cid
FROM enrolled AS e, student AS s
WHERE e.sid = s.sid
GROUP BY e.cid
这是一个较复杂的 SQL 语句,它的执行过程如下:
可以理解成,先将 enrolled 表和 student 表进行笛卡尔积(带上 WHERE 条件),然后根据 e.cid 进行分组,最后对每个分组进行聚合操作。
注意
需要注意的是,在 SELECT 中的字段,如果不是聚合函数,那么它必须出现在 GROUP BY 中。
HAVEING
如果想要对 GROUP BY 的结果进行筛选,可以使用 HAVING 子句。
SQL
-- 这样的语句是可能是错误的,在PostgreSQL中会报错,MYSQL中不会报错
ELECT AVG(s.gpa) AS avg_gpa, e.cid
FROM enrolled AS e, student AS s
WHERE e.sid = s.sid
GROUP BY e.cid
HAVING avg_gpa > 3.9;
-- 正确的写法,虽然写了两次 AVG(s.gpa),但对现代数据库来说,这并不会导致两次计算
SELECT AVG(s.gpa), e.cid
FROM enrolled AS e, student AS s
WHERE e.sid = s.sid
GROUP BY e.cid
HAVING AVG(s.gpa) > 3.9;
聚合如何处理 NULL
这个不是 15445 的内容,但是在实际使用中,可能会遇到。
SUM
,AVG
,MAX
,MIN
函数会忽略 NULL 值,而 COUNT
函数会计算 NULL 值。
如果一个字段中有 NULL 值,那么 COUNT(*)
和 COUNT(字段名)
的结果是不一样的。
COUNT(*)
会计算所有的记录数,而 COUNT(字段名)
会忽略 NULL 值。
String
方言
SQL 语言的方言很多,不同的数据库系统对字符串的处理方式也不尽相同。