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,可以檢視這篇文章