关系数据库标准语言SQL
全部的笔记戳这里,虽然实际项目中用不到这么多 sql 的知识点,但是记录一下,了解一下也OK。
【数据库系统设计】关系数据库标准语言SQL(1)
【数据库系统设计】关系数据库标准语言SQL(3)
数据查询(连接查询)
等值连接 =
Ex:查询每个学生及其选修课程的情况。(等值连接)
SELECT Student.*, SC.*
FROM Student, SC
WHERE Student.Sno = SC.Sno;
自然连接
Ex:查询每个学生及其选修课程的情况。(自然连接)
采用在SELECT
中去掉重复字段的方式实施
SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
FROM Student, SC
WHERE Student.Sno = SC.Sno;
Ex:查询选修2号课程且成绩在70分以上的所有学生的学号和姓名。
SELECT SC.Sno, Student.Sname
FROM Student, SC
WHERE Student.Sno = SC.Sno /*连接谓词*/
AND SC.Grade >= 70 /*选择谓词*/
自身连接
自身连接:一个表与其自己进行连接,是一种特殊的连接
- 需要给表起别名以示区别
- 由于所有属性名都是同名属性,因此必须使用别名前缀
Ex:查询每一门课的直接先修课的名称。
SELECT First.Cname, Second.Cname
/*FROM子句中,可以为表或视图取一别名,这别名只在本句中有效*/
FROM Course First, Course SECOND
WHERE FIRST.Cpno = SECOND.Cno;
外连接 LEFR/RIGHT JOIN ... ON
外连接与普通连接的区别
- 普通连接操作只输出满足连接条件的元组
- 外连接操作以指定表为连接主体,将主体表中不满足连接条件的元组一并输出
- 左外连接
LEFT JOIN
- 列出左边关系中所有的元组
- 右外连接
RIGHT JOIN
- 列出右边关系中所有的元组
select <目标列表达式> [ into 表名 ]
from 表名1 [ inner | right | left | full ] [ outer ]
join 表名2 on 条件
inner join
: 内连接,显示符合 on 指定连接条件的记录
left [outer] join
:左外连接,连接结果中包括左表(表名1)中的所有行,而不仅仅是连接列所匹配的行。如果表名1的某行在表名2中没有匹配行,则在该行新增加的属性上填空值。
right (outer) join
:右外连接,返回右表(表名2)的所有行。如果右表的某行在左表中没有匹配行,则在新增加的属性上填空值。
full (outer) join
: 完全外连接,返回左表和右表中的所有行。当某行在另一个表中没有匹配行时,则另一个表新增加的属性上填空值。
Ex:查询每个学生及其选修课程的情况(外连接)。
SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
FROM Student LEFT JOIN SC ON (Student.Sno=SC.Sno);
多表连接
多表连接:两个以上的表进行连接
Ex:查询每个学生的学号、姓名、选修的课程名及成绩。
SELECT Student.Sno, Sname, Cname, Grade
FROM Student, SC, Course /*多表连接*/
WHERE Student.Sno = SC.Sno AND SC.Cno = Course.Cno;
数据查询(嵌套查询 )
嵌套查询概述
一个SELECT-FROM-WHERE
语句称为一个查询块
将一个查询块嵌套在另一个查询块的WHERE
子句或HAVING
短语的条件中的查询称为嵌套查询
SELECT Sname /*外层查询/父查询*/
FROM Student
WHERE Sno IN /*内层查询/子查询*/
(SELECT Sno
FROM SC WHERE
Cno='2');
上层的查询块称为外层查询或父查询
下层查询块称为内层查询或子查询
SQL语言允许多层嵌套查询
子查询的限制:不能使用ORDER BY
子句
不相关子查询 :子查询的查询条件不依赖于父查询
- 由里向外逐层处理。即每个子查询在上一级查询处理之前求解,子查询的结果用于建立其父查询的查找条件。
相关子查询:子查询的查询条件依赖于父查询
- 首先取外层查询中表的第一个元组,根据它与内层查询相关的属性值处理内层查询,若WHERE子句返回值为真,则取此元组放入结果表
- 然后再取外层表的下一个元组
- 重复这一过程,直至外层表全部检查完为止
带有IN
谓词的子查询
Ex:查询与“刘晨”在同一个系学习的学生。(不相关子查询)
① 确定“刘晨”所在系名
SELECT Sdept
FROM Student WHERE Sname ='刘晨';
② 查找所有在CS系学习的学生。
SELECT Sno, Sname, Sdept FROM Student
WHERE Sdept = 'CS';
将第一步查询嵌入到第二步查询的条件中
SELECT Sno, Sname, Sdept FROM Student
WHERE Sdept IN
(SELECT Sdept
FROM Student
WHERE Sname='刘晨');
Ex:查询与“刘晨”在同一个系学习的学生。 (自连接)
SELECT S1.Sno, S1.Sname,S1.Sdept
FROM Student S1, Student S2
WHERE S1.Sdept = S2.Sdept
AND S2.Sname = '刘晨'; /*这里不能写S1.Sname*/
Ex:查询选修了课程名为“信息系统”的学生学号和姓名。
SELECT Sno, Sname/*3最后在Student关系中取出Sno和Sname */
FROM Student
WHERE Sno IN
(SELECT Sno FROM/*2然后在SC关系中找出选修了3号课程的学生学号*/
SC WHERE Cno IN
(SELECT Cno/*1首先在Course关系中找出“信息系统”的课程号,为3号*/
FROM Course
WHERE Cname= '信息系统'
)
);
Ex:查询选修了课程名为“信息系统”的学生学号和姓名。(连接查询)
SELECT Student.Sno, Sname
FROM Student, SC, Course
WHERE Student.Sno = SC.Sno
AND SC.Cno = Course.Cno
AND Course.Cname = '信息系统';
带有比较运算符的子查询
当能确切知道内层查询返回单值时,可用比较运算符(>
,<
,=
,>=
,<=
,!=
或<>
)。
Ex:查询与“刘晨”在同一个系学习的学生。
SELECT Sno,Sname,Sdept
FROM Student
WHERE Sdept = /*由于一个学生只可能在一个系学习 , 用=代替*/
(SELECT Sdept
FROM Student
WHERE Sname = '刘晨');
Ex:找出每个学生超过他选修课程平均成绩的课程号。( 相关子查询 )
SELECT Sno, Cno
FROM SC x
WHERE Grade >= (SELECT AVG(Grade)/*相关子查询*/
FROM SC y
WHERE y.Sno = x.Sno);
可能的执行过程:
(1) 从外层查询中取出 SC 的一个元组 x,将元组 x 的 Sno 值(201215121)传送给内层查询。
SELECT AVG(Grade)
FROM SC y
WHERE y.Sno = '201215121';
(2)执行内层查询,得到值 88.33(近似值),用该值代替内层查询,得到外层查询:
SELECT Sno,Cno
FROM SC x
WHERE Grade >=88.33;
(3)执行该查询,得到结果:201215121, 1
然后外层查询取出下一个元组重复做上述(1)(2)(3)步骤,直到外层的 SC 元组全部处理完毕。
得到结果:
(201215121,1)
(201215122,2)
带有ANY
或ALL
谓词的子查询
使用ANY
或ALL
谓词时必须同时使用比较运算
语义为:
>ANY
:大于子查询结果中的某个值>ALL
:大于子查询结果中的所有值< ANY
:小于子查询结果中的某个值< ALL
:小于子查询结果中的所有值>= ANY
:大于等于子查询结果中的某个值>= ALL
:大于等于子查询结果中的所有值
Ex:查询非计算机科学系中比计算机科学系任意一个学生年龄小的学生姓名和年龄(ANY
实现)
SELECT Sname, Sage
FROM Student
WHERE Sage < ANY(SELECT Sage
FROM Student
WHERE Sdept = 'CS')
AND Sdept <> 'CS';/*父查询块中的条件*/
执行过程:
(1) 首先处理子查询,找出CS系中所有学生的年龄,构成一个集合(20,19)
(2) 处理父查询,找所有不是CS系且年龄小于 20 或 19 的学生
Ex:查询非计算机科学系中比计算机科学系任意一个学生年龄小的学生姓名和年龄(聚集函数)
SELECT Sname,Sage
FROM Student
WHERE Sage <
(SELECT MAX(Sage)
FROM Student
WHERE Sdept= 'CS')
AND Sdept <> 'CS';
Ex:查询非计算机科学系中比计算机科学系所有学生年龄都小的学生姓名及年龄。
方法一:ALL
谓词
SELECT Sname,Sage
FROM Student
WHERE Sage < ALL
(SELECT Sage
FROM Student
WHERE Sdept= 'CS')
AND Sdept <> 'CS';
方法二:聚集函数
SELECT Sname,Sage
FROM Student
WHERE Sage <
(SELECT MIN(Sage)
FROM Student
WHERE Sdept = 'CS')
AND Sdept <> 'CS';
ANY
(或SOME
),ALL
谓词与聚集函数、IN
谓词的等价转换关系
带有EXISTS
谓词的子查询
EXISTS
谓词
- 存在量词
- 带有
EXISTS
谓词的子查询不返回任何数据,只产生逻辑真值true
或逻辑假值false
。- 若内层查询结果非空,则外层的
WHERE
子句返回真值 - 若内层查询结果为空,则外层的
WHERE
子句返回假值
- 若内层查询结果非空,则外层的
- 由
EXISTS
引出的子查询,其目标列表达式通常都用*
,因为带EXISTS
的子查询只返回真值或假值,给出列名无实际意义。
NOT EXISTS
谓词
- 若内层查询结果非空,则外层的
WHERE
子句返回假值 - 若内层查询结果为空,则外层的
WHERE
子句返回真值
Ex:查询所有选修了1号课程的学生姓名。
方法一:EXISTS
谓词
思路分析:
本查询涉及 Student 和 SC 关系
在 Student 中依次取每个元组的 Sno 值,用此值去检查 SC 表
若 SC 中存在这样的元组,其 Sno 值等于此 Student.Sno 值,并且其 Cno= ‘1’,则取此 Student.Sname 送入结果表
SELECT Sno, Sname
FROM Student
WHERE EXISTS
(SELECT *
FROM SC
WHERE SC.Sno = Student.Sno AND SC.Cno= '1');
方法二:连接查询
Select Sno, Sname
From Student, SC
Where Student.Sno=SC.Sno and Cno='1';
方法三:IN
嵌套查询
Select Sno, Sname
From Student
Where Sno IN
(Select Sno From SC Where Cno ='1');
Select Sno, Sname
From Student
Where '1' IN
(Select Cno From SC Where Sno = Student.Sno);
Ex:查询没有选修1号课程的学生姓名。
无法使用连接查询完成。
SELECT Sname
FROM Student
WHERE NOT EXISTS
(SELECT *
FROM SC
WHERE Sno = Student.Sno AND Cno='1');
Ex:查询选修了全部课程的学生姓名。
SELECT Sname
FROM Student
WHERE NOT EXISTS
(SELECT *
FROM Course
WHERE NOT EXISTS
(SELECT *
FROM SC
WHERE Sno= Student.Sno
AND Cno= Course.Cno));
数据查询(集合查询)
集合操作的种类
- 并操作
UNION
- 交操作
INTERSECT
- 差操作
EXCEPT
参加集合操作的各查询结果的列数必须相同;
对应项的数据类型也必须相同。
UNION
:将多个查询结果合并起来时,系统自动去掉重复元组
UNION ALL
:将多个查询结果合并起来时,保留重复元组
Ex:查询计算机科学系的学生及年龄不大于19岁的学生。
SELECT *
FROM Student
WHERE Sdept='CS'
UNION
SELECT *
FROM Student
WHERE Sage<=19;
Ex:查询选修了课程1或者选修了课程2的学生。
SELECT Sno
FROM SC
WHERE Cno='1'
UNION
SELECT Sno
FROM SC
WHERE Cno= '2';
Ex:查询计算机科学系的学生与年龄不大于19岁的学生的交集。
方法一:
mysql中会报错,原因是Mysql不支持INTERSECT
交集操作
SELECT *
FROM Student
WHERE Sdept='CS'
INTERSECT
SELECT *
FROM Student
WHERE Sage<=19;
方法二:实际上就是查询计算机科学系中年龄不大 于19岁的学生。
SELECT *
FROM Student
WHERE Sdept= 'CS' AND Sage<=19;
Ex:查询既选修了课程1又选修了课程2的学生。
方法一:
mysql中会报错,原因是Mysql不支持INTERSECT
交集操作
SELECT Sno
FROM SC
WHERE Cno='1'
INTERSECT
SELECT Sno
FROM SC
WHERE Cno='2';
方法二:
SELECT Sno
FROM SC
WHERE Cno='1'
AND Sno IN
(SELECT Sno
FROM SC
WHERE Cno='2');
方法三:自连接
Select X.Sno
From SC as X, SC as Y
Where X.Sno=Y.Sno
AND X.Cno='1'
AND Y.Cno='2';
Ex:查询计算机科学系的学生与年龄不大于19岁的学生的差集。
方法一:
mysql中会报错,原因是Mysql不支持EXCEPT
差集操作
SELECT *
FROM Student
WHERE Sdept='CS'
EXCEPT
SELECT *
FROM Student
WHERE Sage <=19;
方法二:
实际上是查询计算机科学系中年龄大于19岁的学生。
SELECT *
FROM Student
WHERE Sdept= 'CS' AND Sage>19;
基于派生表的查询
子查询不仅可以出现在WHERE
子句中,还可以出现在FROM
子句中,这时子查询生成的临时派生表(Derived Table)成为主查询的查询对象
Ex:找出每个学生超过他自己选修课程平均成绩的课程号 ,可改写为:
SELECT Sno, Cno
FROM SC, (SELECT Sno avg_sno, Avg(Grade) avg_grade
FROM SC
GROUP BY Sno) AS Avg_sc
WHERE SC.Sno = Avg_sc.avg_sno
and SC.Grade >= Avg_sc.avg_grade;
如果子查询中没有聚集函数,派生表可以不指定属性列,子查询SELECT
子句后面的列名为其缺省属性。
Ex:查询所有选修了1号课程的学生姓名,可以用如下查询完成:
SELECT Sname
FROM Student,
(SELECT Sno FROM SC WHERE Cno='1') AS SC1
WHERE Student.Sno=SC1.Sno;
课后作业
学了全部课程的学生的姓名,用分组语句来完成:
SELECT Sname
FROM student
WHERE Sno IN
(
SELECT Sno
FROM SC
GROUP BY Sno
HAVING COUNT(*) = /*学生选课数 = 总课程数*/
(SELECT COUNT(*) FROM course) /*7*/
);