SELECT语句详细分析
文章目录
单表查询
SELECT * / 列名 / 聚集函数(列名)/ 算术表达式 / ‘字符串常量’ /
这里的*
号代表全部。
统计函数(聚集函数)
聚集函数只能用于SELECT
子句和GROUP BY
中的HAVING
子句,而不能用于WHERE
子句。
函数 | 说明 |
---|---|
AVG() | 指定列的平均值 |
COUNT() | 指定列的统计行数 |
MAX() | 指定列的最大值 |
MIN() | 指定列的最小值 |
SUM() | 指定列的和 |
WHERE子句
SELECT *
FROM Student
WHERE
查询条件 | 谓词 |
---|---|
确定范围 | BETWEEN AND, NOT BETWEEN AND |
确定集合 | IN,NOT IN |
字符匹配 | LIKE ,NOT LIKE |
空值 | IS NULL, IS NOT NULL |
在SQL中模糊查询的方式主要有两种,一种是通配符,另一种是正则表达式。它们的不同很简单,通配符是可以一对多,而正则表达式这是一一对应,理解为一种“格式”。具体可以参考这篇一文理解通配符和正则表达式
正常情况使用=
,<>
来判断。有通配符时,使用LIKE关键字。
ORDER/GROUP BY子句
ORDER BY
子句
SELECT Sno,Grade
FROM Sc
WHERE Cno='2'
ORDER BY Grade[ASC|DESC];
GROUP BY
我们知道聚集函数或者函数表达式是针对某一列进行,而GROUP BY
是为了细化聚集函数的作用对象,将列分组,然后作用于每一组。
求每个课程号即相应的选课人数
SELECT Cno,COUNT(Sno)
FROM SC
GROUP BY Cno;
HAVING子句
HAVING
在分组的基础上作进一步的筛选
选课门大于2的学生学号
SELECT Sno,COUNT(Cno)
FROM Student
GROUP BY Sno
HAVING COUNT(*)>2;
平均成绩大于90的学号
SELECT Sno,AVG(Grade)
FROM SC
GROUP BY Sno
HAVING AVG(Grade)>90;
WHERE
在GROUP BY
之前操作,HAVING
在GROUP BY
之后进行,WHERE
是对元组进行过滤,而HAVING
是对分组进行过滤。
女生人数大于10的系
SELECT Sdept,COUNT(*)
FROM Student
WHERE Ssex='女'
GROUP BY Sdept
HAVING COUNT(*)>10;
连接查询
等值连接
SELECT Student.*,SC.*
FROM Student,SC
WHERE Student.Sno=SC.Sno;
SELECT Student.Sno,SC.Sname
FROM Student,SC
WHERE Student.Sno=SC.Sno AND Cno=2 AND Grade >90;
自连接
- 需要给表起别名以区别
- 因为有同名属性,因此必须使用表名前缀
比如查询先修课的先修课,使用自连接。
表名和属性名的别名都是使用关键字AS
。
SELECT First.Cno,Second.Cpno
FROM Course AS First,Course AS Second
WHERE First.Cpno=Second.Cno
外连接
有悬浮元组。缺失属性补NULL。
多表连接
由二到多的一个推广。
嵌套查询
查询块:一个SELECT-FROM-WHERE
语句。
嵌套查询也称子查询,说白了就是查询套查询。
=
只是单值判断,而子查询的返回值未必是一个,IN
是范围,因此IN更合理
分类
-
子查询是独立进行,不相关子查询
一般是可以用连接替代的
-
子查询依赖父查询的,送条件
因为子查询不只是一个结果,而是两方有关联。这是一般的连接是不能替代的。
SELECT Sno,Cno FROM SC AS x WHERE Grade >=(SELECT AVG(Grade) FROM SC y WHERE y.Sno=x.Sno)
ANY
与ALL
的用法
ANY
是其中一个,ALL
是所有
完全可以通过聚集函数MAX()
MIN()
替代
EXISTS
返回真值
相关子查询
SELECT Sname
FROM Student
WHERE EXISTS
(SELECT *
FROM SC
WHERE Sno=Student.Sno AND Cno='1');
NOT EXISTS
SELECT Sname
FROM Student
WHERE NOT EXISTS
(SELECT *
FROM SC
WHERE Sno=Student.Sno AND Cno='1');
通过EXISTS
实现全称量词
∀ P = ¬ ( ∃ x ( ¬ P ) ) {\forall}P={\lnot}({\exists}x(\lnot P)) ∀P=¬(∃x(¬P))
查询选修了所有课程的学生姓名
SELECT Sname
FROM Student
WHERE NOT EXISTS
(SELECT *
FROM Course
WHERE NOT EXISTS
(SELECT *
FROM SC
WHERE Sno=Student.Sno
AND Cno=Course.Cno));
理解这个SQL语句,第一,每一次SELECT
返回的都是一个集合,这个集合可能只有一个元素,也可能是空集NULL
。非空集对应真值1
,即存在EXISTS
,而空集NULL
对应真值0
,即不存在。
那我们可以这样分析,先看最里面,给定一个学生,判断所有课程,相当于一个连接判断是否存在,如果满足条件,则不返回该门课程,所有课程都满足都不返回,返回一个空集NULL
即0,再结果一次求反,返回1,输出学生姓名。一旦有一门不满足条件,说明最里面返回的不是空集即1,再一次求反,返回0,不输出学生姓名。
通过EXISTS
实现逻辑蕴含
查询至少选修了学生201215122选修的全部课程的学生号码
∀ ( y ) p → q = ¬ ( ∃ y ( ¬ ( ¬ p ∨ q ) ) ) = ¬ ∃ y ( p ∧ ¬ q ) {\forall}(y)p {\rightarrow}q={\lnot}({\exists}y({\lnot({\lnot}p{\lor}q)}))={\lnot}{\exists}y(p{\land}\lnot q) ∀(y)p→q=¬(∃y(¬(¬p∨q)))=¬∃y(p∧¬q)
把父查询的信息,一层层送进去
SELECT DISTINCT Sname
FROM SC AS SCX
WHERE NOT EXISTS(
SELECT *
FROM SC AS SCY
WHERE SCY.Sno='201215122'
AND NOT EXISTS
(SELECT * #EXISTS后要跟一个完整的查询块,返回真值,这里只是指定从哪个表找,并无实际返回
FROM SC AS SCZ #意义,返回什么都无所谓,所以用*。FROM 表只是中间作用
WHERE SCZ.Sno = SCX.Sno AND
SCZ.Cno=SCY.Cno
)
);
集合查询
集合操作
每次SELECT
返回的结果其实就是元组的集合。因此下列集合操作可以用在各个select
语句之间。但是要注意格式要相同,元组列数和数据类型。
并UNION
(DISTINCT OR),交INTERSECT
,差操作EXCEPT
基于派生表的查询
子查询出现再FROM子句中的,子查询生成临时表作为主查询的查询对象。