CMU15445 Lecture 2 Advanced SQL
本节介绍SQL的高级用法
关系型语言
当用户使用声明式语言,他只需要说明他所需要的结果。DBMS
会优化产生结果的过程。
Relational algebra
是基于集合(无序,不可重复),而SQL
是基于背包(无序,可重复)
SQL历史
SQL
来源于IBM的System R
项目。其主要由三种命令组成:
DML
(Data Manipulation Language),SELECT, INSERT, UPDATE 和 DELETE等语句DDL
(Data Definition Language),对于表,索引,视图,或其它对象的Schema的定义,就是建表,建索引的语句之类的DCL
(Data Control Language),权限控制,访问控制- 当然还有一些操作,诸如view的定义,完整性或引用约束,事务的开启与关闭等
SQL的一些高级语法
展示语法所用的数据库实例:
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)
);
Aggreates
- MAX(col),返回一列中值的最大值
- MIN(col),返回一列中值的最小值
- AVG(col),返回一列中值的平均值
- SUM(col),返回一列中值的和
- COUNT(col),用于返回一列中值的#
如果想在student表中查询有着以'@cs'结尾的login的学生的数量,可以这样写(有三种方式)
SELECT COUNT(*) FROM student WHERE login LIKE '%@cs';
SELECT COUNT(login) FROM student WHERE login LIKE '%@cs';
SELECT COUNT(1) FROM student WHERE login LIKE '%@cs';
也可以在一条SELECT语句中使用多个aggregates函数
SELECT AVG(gpa), COUNT(sid)
FROM student WHERE login LIKE '%@cs';
一些aggreagate函数支持DISTINCT
关键字
SELECT COUNT(DISTINCT login)
FROM student WHERE login LIKE '%@cs';
输出相对于一个aggregate的其他列,会出错,该语句中e.cid是未定义的
SELECT AVG(s.gpa), e.cid
FROM enrolled AS e, student AS s
WHERE e.sid = s.sid;
这个时候需要用到group by
命令,
SELECT AVG(s.gpa), e.cid
FROM enrolled AS e, student AS s
WHERE e.sid = s.sid
GROUP BY e.cid;
如此一来就可以正确的显示了,将tuples按照group by
划分为子集,再对子集做aggregate
另外,可以使用HAVING
子句对aggregation之后的结果做过滤,HAVIN就像是GROUP BY的WHERE子句
SELECT 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;
字符串操作
虽然MYSQL对于字母大小写不敏感,并且使用双引号与单引号皆可,但其他SQL不是这样的
模式匹配:
- "%"匹配任何的子串(包括空串)
- "_"匹配任意一个字符
字符串连接: - "||"用来连接两个字符串
输出重定向
除了将查询结果显示到终端,可以将结果放一个新的表中,可以用如下语句:
- 新建表:
SELECT DISTINCT cid INTO CourseIds FROM enrolled;
- 对于已经存在的表,可以将SELECT插入到数据库中已经存在的表中,SELECT的查询结果必须与表的列的数量与属性的类型一致,但是列名可以不一样:
INSERT INTO CourseIds (SELECT DISTINCT cid FROM enrolled);
输出控制
可以用ORDER BY
对SQL的结果进行排序,可以用谓词ASC
与DESC
与属性去对结果做想要的排序
SELECT sid FROM enrolled WHERE cid = '15-721' ORDER BY grade DESC;
SELECT sid FROM enrolled WHERE cid = '15-721' ORDER BY grade DESC, sid ASC;
SELECT sid FROM enrolled WHERE cid = '15-721' ORDER BY UPPER(grade) DESC, sid + 1 ASC;
可以用LIMIT
子句限定查询结果的tuple的数量
SELECT sid, name FROM student WHERE login LIKE '%@cs'
LIMIT 10;
也可以提供一个位移来到达获取一个范围的结果
SELECT sid, name FROM student WHERE login LIKE '%@cs' LIMIT 10 OFFSET 20;
如果不使用ORDER BY
,那么使用LIMIT
每次返回的结果可能不同
内嵌查询
inner queries和out queries组成一个nested queries,inner queries可以引用outer queries中的内容,但反过来不行
一个例子是,获取注册了15-445的学生的名字
SELECT name FROM student WHERE sid IN ( SELECT sid FROM enrolled WHERE cid = '15-445' );
内嵌查询支持的谓词:
- ALL: 所有 inner queries 返回的记录都必须满足条件
- ANY:任意 inner queries 返回的记录满足条件即可
- IN:与
= ANY()
等价 - EXISTS:inner queries 返回的表不为空
ANY的用法,获取注册了15-445的学生的名字
SELECT name FROM student
WHERE sid = ANY (
SELECT sid FROM enrolled
WHERE cid = '15-445'
)
ALL,IN的用法,找到至少参与一门课程的所有学生中,id 最大的
SELECT sid, name FROM student
WHERE sid >= ALL (
SELECT sid FROM enrolled
);
SELECT sid, name FROM student
WHERE sid IN (
SELECT MAX(sid) FROM enrolled
);
SELECT sid, name FROM student
WHERE sid IN (
SELECT sid FROM enrolled
ORDER BY sid DESC LIMIT 1
);
NOT 的用法
SELECT * FROM course
WHERE NOT EXISTS (
SELECT * FROM enrolled
WHERE course.cid = enrolled.cid
);
nested queries 比较难被优化(具体原因暂不知道)
DATA/TIME OPERAIONS
用来操作与修改DATA/TIME属性,每个SQL的用法差别极大
窗口函数
类似于聚合函数,但是会保留原tuple的所有元素
Common Table Expressions(CTE)
CTE
就像对于一次查询的生成一个临时的表,使用WITH
子句来绑定一个子查询的结果给WITH
子句所给定的名字
关于递归CTE,可以查看这篇文章
参考
note2