diff --git a/README.md b/README.md index 146b4f1..cf22107 100755 --- a/README.md +++ b/README.md @@ -6,11 +6,7 @@ 长期维护更新~ -- 2019.6.23 更新数据集与部分SQL查询题目 - ---- - -[2019.6.23 更新内容直戳点 ](./day1_dataset.md) +- [MySQL实战系列](./prac) ## 3.关于我 diff --git a/img/dataset.png b/img/dataset.png new file mode 100644 index 0000000..594b69a Binary files /dev/null and b/img/dataset.png differ diff --git a/img/ra.png b/img/ra.png new file mode 100644 index 0000000..cbb869b Binary files /dev/null and b/img/ra.png differ diff --git a/img/sql.png b/img/sql.png new file mode 100644 index 0000000..5453ba6 Binary files /dev/null and b/img/sql.png differ diff --git a/img/use.png b/img/use.png new file mode 100644 index 0000000..0a6db39 Binary files /dev/null and b/img/use.png differ diff --git a/prac/README.md b/prac/README.md new file mode 100644 index 0000000..0744dee --- /dev/null +++ b/prac/README.md @@ -0,0 +1,25 @@ +# 实战系列 + +[实战1:MySQL建库及建表及部分查询](./实战1.md) + +[实战2:MySQL查询](./实战2.md) + +[实战3:MySQL实现窗口函数及案例](./实战3.md) + +[实战4:MySQL查询](./实战4.md) + +[实战5:关系代数与sql推荐网站与实战](./实战5.md) + +[实战6:关系代数与三种连接](./实战6.md) + +[实战7:你不知道的外键与约束使用](./实战7.md) + +## 关于我 + +- 个人网站 + + https://light-city.club + +- 个人微信公众号 + +![wechat](../img/wechat.jpg) diff --git a/day1_dataset.md "b/prac/\345\256\236\346\210\2301.md" old mode 100755 new mode 100644 similarity index 85% rename from day1_dataset.md rename to "prac/\345\256\236\346\210\2301.md" index 5030718..6bdb705 --- a/day1_dataset.md +++ "b/prac/\345\256\236\346\210\2301.md" @@ -1,6 +1,6 @@ -# Day1-数据库创建与表创建及一些查询 +# MySQL实战上车,Github仓库Star起来 -## 0.关于我 +## 关于我 - 个人网站 @@ -8,7 +8,7 @@ - 个人微信公众号 -![wechat](./img/wechat.jpg) +![wechat](../img/wechat.jpg) ## 1.创建数据库 @@ -301,8 +301,6 @@ mysql> select * from SC sc where sc.CId='01' and sc.SId in (select sc.SId from S 5 rows in set (0.00 sec) ``` - - > 查询存在" 01 "课程但可能不存在" 02 "课程的情况(不存在时显示为 null ) 首先要明确查询的表是成绩表(`SC`)的信息,这里的主要难点是:不存在显示为NULL。 @@ -389,58 +387,3 @@ mysql> select * from (select * from SC sc where sc.CId='02')s1 left join (select +-----+-----+-------+------+------+-------+ 1 row in set (0.00 sec) ``` - -> 查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩 - -```mysql -mysql> select s.SId,s.Sname,avg(sc.score) as AVG from Student s join SC sc on s.SId=sc.SId group by sc.SID having AVG>=60; -+-----+--------+----------+ -| SId | Sname | AVG | -+-----+--------+----------+ -| 01 | 赵雷 | 89.66667 | -| 02 | 钱电 | 70.00000 | -| 03 | 孙风 | 80.00000 | -| 05 | 周梅 | 81.50000 | -| 07 | 郑竹 | 93.50000 | -+-----+--------+----------+ -5 rows in set (0.00 sec) -``` - -> 查询在 SC 表存在成绩的学生信息 - -方法一:使用`distinct`关键字 - -```mysql -mysql> select distinct s.* from Student s, SC sc where s.SId=sc.SId; -+-----+--------+---------------------+------+ -| SId | Sname | Sage | Ssex | -+-----+--------+---------------------+------+ -| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | -| 02 | 钱电 | 1990-12-21 00:00:00 | 男 | -| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | -| 04 | 李云 | 1990-12-06 00:00:00 | 男 | -| 05 | 周梅 | 1991-12-01 00:00:00 | 女 | -| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | -| 07 | 郑竹 | 1989-01-01 00:00:00 | 女 | -+-----+--------+---------------------+------+ -7 rows in set (0.00 sec) -``` - -方法二:使用`exists`关键字 - -```mysql -mysql> select * from Student s where exists(select sc.SId from SC sc where s.SId=sc.SId); -+-----+--------+---------------------+------+ -| SId | Sname | Sage | Ssex | -+-----+--------+---------------------+------+ -| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | -| 02 | 钱电 | 1990-12-21 00:00:00 | 男 | -| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | -| 04 | 李云 | 1990-12-06 00:00:00 | 男 | -| 05 | 周梅 | 1991-12-01 00:00:00 | 女 | -| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | -| 07 | 郑竹 | 1989-01-01 00:00:00 | 女 | -+-----+--------+---------------------+------+ -7 rows in set (0.00 sec) -``` - diff --git "a/prac/\345\256\236\346\210\2302.md" "b/prac/\345\256\236\346\210\2302.md" new file mode 100644 index 0000000..d421293 --- /dev/null +++ "b/prac/\345\256\236\346\210\2302.md" @@ -0,0 +1,492 @@ +## 关于我 + +- 个人网站 + + https://light-city.club + +- 个人微信公众号 + +![wechat](../img/wechat.jpg) + +> 查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩 + +```mysql +mysql> select s.SId,s.Sname,avg(sc.score) as AVG from Student s join SC sc on s.SId=sc.SId group by sc.SID having AVG>=60; ++-----+--------+----------+ +| SId | Sname | AVG | ++-----+--------+----------+ +| 01 | 赵雷 | 89.66667 | +| 02 | 钱电 | 70.00000 | +| 03 | 孙风 | 80.00000 | +| 05 | 周梅 | 81.50000 | +| 07 | 郑竹 | 93.50000 | ++-----+--------+----------+ +5 rows in set (0.00 sec) +``` + +> 查询在 SC 表存在成绩的学生信息 + +方法一:使用`distinct`关键字 + +```mysql +mysql> select distinct s.* from Student s, SC sc where s.SId=sc.SId; ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | +| 02 | 钱电 | 1990-12-21 00:00:00 | 男 | +| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | +| 04 | 李云 | 1990-12-06 00:00:00 | 男 | +| 05 | 周梅 | 1991-12-01 00:00:00 | 女 | +| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | +| 07 | 郑竹 | 1989-01-01 00:00:00 | 女 | ++-----+--------+---------------------+------+ +7 rows in set (0.00 sec) +``` + +方法二:使用`exists`关键字 + +```mysql +mysql> select * from Student s where exists(select sc.SId from SC sc where s.SId=sc.SId); ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | +| 02 | 钱电 | 1990-12-21 00:00:00 | 男 | +| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | +| 04 | 李云 | 1990-12-06 00:00:00 | 男 | +| 05 | 周梅 | 1991-12-01 00:00:00 | 女 | +| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | +| 07 | 郑竹 | 1989-01-01 00:00:00 | 女 | ++-----+--------+---------------------+------+ +7 rows in set (0.00 sec) +``` + +> 查询所有同学的学生编号、学生姓名、选课总数、所有课程的成绩总和 + +```mysql +mysql> select s.SId,s.Sname,count(s.SId) '选课总和',sum(sc.score) '成绩总和' from Student s left join SC sc on s.SId=sc.SId group by s.SId; ++-----+--------+--------------+--------------+ +| SId | Sname | 选课总和 | 成绩总和 | ++-----+--------+--------------+--------------+ +| 01 | 赵雷 | 3 | 269.0 | +| 02 | 钱电 | 3 | 210.0 | +| 03 | 孙风 | 3 | 240.0 | +| 04 | 李云 | 3 | 100.0 | +| 05 | 周梅 | 2 | 163.0 | +| 06 | 吴兰 | 2 | 65.0 | +| 07 | 郑竹 | 2 | 187.0 | +| 09 | 张三 | 1 | NULL | +| 10 | 李四 | 1 | NULL | +| 11 | 李四 | 1 | NULL | +| 12 | 赵六 | 1 | NULL | +| 13 | 孙七 | 1 | NULL | ++-----+--------+--------------+--------------+ +12 rows in set (0.00 sec) +``` + +

注意:题目中说的是查询所有同学的相关信息,然而有些同学的信息在SC成绩表中是没有的,因为他们有可能没有成绩,这些人的数据也需要被查询出来,所有需要左连接查询!

+ +> 查询成绩的学生信息 + +分别使用`in`与`exists`子查询: + +```mysql +mysql> select * from Student s where s.SId in(select sc.SId from SC sc); ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | +| 02 | 钱电 | 1990-12-21 00:00:00 | 男 | +| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | +| 04 | 李云 | 1990-12-06 00:00:00 | 男 | +| 05 | 周梅 | 1991-12-01 00:00:00 | 女 | +| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | +| 07 | 郑竹 | 1989-01-01 00:00:00 | 女 | ++-----+--------+---------------------+------+ +7 rows in set (0.00 sec) + +mysql> select * from Student s where exists(select sc.SId from SC sc where s.SId=sc.SId); ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | +| 02 | 钱电 | 1990-12-21 00:00:00 | 男 | +| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | +| 04 | 李云 | 1990-12-06 00:00:00 | 男 | +| 05 | 周梅 | 1991-12-01 00:00:00 | 女 | +| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | +| 07 | 郑竹 | 1989-01-01 00:00:00 | 女 | ++-----+--------+---------------------+------+ +7 rows in set (0.00 sec) +``` + +> 查询「李」姓老师的数量 + +```mysql +mysql> select count(t.TId) '数量' from Teacher t where t.Tname like '李%'; ++--------+ +| 数量 | ++--------+ +| 1 | ++--------+ +1 row in set (0.00 sec) +``` + +> 查询学过「张三」老师授课的同学的信息 + +考察:多表联合查询 + +涉及四张表! + +```mysql +mysql> select s.* from Student s,Course c,Teacher t,SC sc where t.TId=c.TId and c.CId=sc.CId and sc.SId=s.SId and t.Tname='张三'; ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | +| 02 | 钱电 | 1990-12-21 00:00:00 | 男 | +| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | +| 04 | 李云 | 1990-12-06 00:00:00 | 男 | +| 05 | 周梅 | 1991-12-01 00:00:00 | 女 | +| 07 | 郑竹 | 1989-01-01 00:00:00 | 女 | ++-----+--------+---------------------+------+ +6 rows in set (0.00 sec) +``` + +> 查询没有学全所有课程的同学的信息 + +因为有学生什么课都没有选,反向思考,先查询选了所有课的学生,再选择这些人之外的学生。 + +```mysql +mysql> select s.* from Student s where s.SId not in(select sc.SId from SC sc group by sc.SId having count(sc.SId)=(select count(c.CId) from Course c)); ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 05 | 周梅 | 1991-12-01 00:00:00 | 女 | +| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | +| 07 | 郑竹 | 1989-01-01 00:00:00 | 女 | +| 09 | 张三 | 2017-12-20 00:00:00 | 女 | +| 10 | 李四 | 2017-12-25 00:00:00 | 女 | +| 11 | 李四 | 2012-06-06 00:00:00 | 女 | +| 12 | 赵六 | 2013-06-13 00:00:00 | 女 | +| 13 | 孙七 | 2014-06-01 00:00:00 | 女 | ++-----+--------+---------------------+------+ +8 rows in set (0.00 sec) +``` + +这道题进一步思考,如果要查询没有学全所有课程的同学的信息当中每个学生的选课个数。 + +

注意:你或许第一反应,这还不简单,直接与SC成绩表联合查询不就得了,可是那如果当学生没有选择任何课程的时候,这些信息是查不到的,也就是只能输出查询到的学生信息,如下所示:

+ +```mysql +mysql> select s1.*,count(s1.SId) from (select s.* from Student s where s.SId not in(select sc.SId from SC sc group by sc.SId having count(sc.SId)=(select count(c.CId) from Course c))) s1 join SC sc on s1.SId=sc.SId group by s1.SId; ++-----+--------+---------------------+------+---------------+ +| SId | Sname | Sage | Ssex | count(s1.SId) | ++-----+--------+---------------------+------+---------------+ +| 05 | 周梅 | 1991-12-01 00:00:00 | 女 | 2 | +| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | 2 | +| 07 | 郑竹 | 1989-01-01 00:00:00 | 女 | 2 | ++-----+--------+---------------------+------+---------------+ +3 rows in set (0.00 sec) +``` + +

注意:那换成left join不就行了嘛,真的可以?如下所示:上述查询的count(sc.SId)不能为count(s.SId),如果写成这个,也会查出与理想不符合,因为不管SC成绩表中有没有数据,count(s.SId)至少为1,而实际情况可以为0。

+ +```mysql +mysql> mysql> select s1.*,count(s1.SId) from (select s.* from Student s where s.SId not in(select sc.SId from SC sc group by sc.SId having count(sc.SId)=(selec(c.CId) from Course c))) s1 left join SC sc on s1.SId=sc.SId group by s1.SId; ++-----+--------+---------------------+------+---------------+ +| SId | Sname | Sage | Ssex | count(s1.SId) | ++-----+--------+---------------------+------+---------------+ +| 05 | 周梅 | 1991-12-01 00:00:00 | 女 | 2 | +| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | 2 | +| 07 | 郑竹 | 1989-01-01 00:00:00 | 女 | 2 | +| 09 | 张三 | 2017-12-20 00:00:00 | 女 | 1 | +| 10 | 李四 | 2017-12-25 00:00:00 | 女 | 1 | +| 11 | 李四 | 2012-06-06 00:00:00 | 女 | 1 | +| 12 | 赵六 | 2013-06-13 00:00:00 | 女 | 1 | +| 13 | 孙七 | 2014-06-01 00:00:00 | 女 | 1 | ++-----+--------+---------------------+------+---------------+ +8 rows in set (0.00 sec) +``` + +

上述查询的count(sc.SId)不能为count(s.SId),如果写成这个,也会查出与理想不符合,因为不管SC成绩表中有没有数据,count(s.SId)至少为1,而实际情况可以为0。最后修改如下:

+ +```mysql +mysql> select s1.*,count(sc.SId) from (select s.* from Student s where s.SId not in(select sc.SId from SC sc group by sc.SId having count(sc.SId)=(select count(c.CId) from Course c))) s1 left join SC sc on s1.SId=sc.SId group by s1.SId; ++-----+--------+---------------------+------+---------------+ +| SId | Sname | Sage | Ssex | count(sc.SId) | ++-----+--------+---------------------+------+---------------+ +| 05 | 周梅 | 1991-12-01 00:00:00 | 女 | 2 | +| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | 2 | +| 07 | 郑竹 | 1989-01-01 00:00:00 | 女 | 2 | +| 09 | 张三 | 2017-12-20 00:00:00 | 女 | 0 | +| 10 | 李四 | 2017-12-25 00:00:00 | 女 | 0 | +| 11 | 李四 | 2012-06-06 00:00:00 | 女 | 0 | +| 12 | 赵六 | 2013-06-13 00:00:00 | 女 | 0 | +| 13 | 孙七 | 2014-06-01 00:00:00 | 女 | 0 | ++-----+--------+---------------------+------+---------------+ +8 rows in set (0.00 sec) +``` + +

综上:我们可以得到这个结论:当直接连接查询的时候,对于select的结果不需要考虑来源于哪个表,但是当有左或右连接时,需要慎重考虑来自于哪个表!

+ +> 查询至少有一门课与学号为" 01 "的同学所学相同的同学的信息 + +这个看上去有点绕,实际上很简单,这样子思考: + +理解“所学相同”,就是课程Id相同。因为只让查询出至少一门课与学号"01"的同学所学相同,而不是查询出与学号"01"的同学所学完全相同,所以可以将查询出"01"的所有课程编号作为子查询,然后又由于是至少一门课程,这表示,就是普通的连接,而不是左或右连接,因为当学生表与课程表进行连接后,有可能一门课都没。 + +(1)首先查询出学号为"01"的同学所学课程编号 + +(2)连接查询 + +(3)连接查询+子查询(1) + +方法一: + +将两边连接使用in来查询: + +```mysql +mysql> select * from Student s where s.SId in (select sc.SId from SC sc where sc.CId in (select sc.CId from SC sc where sc.SId='01')); ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | +| 02 | 钱电 | 1990-12-21 00:00:00 | 男 | +| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | +| 04 | 李云 | 1990-12-06 00:00:00 | 男 | +| 05 | 周梅 | 1991-12-01 00:00:00 | 女 | +| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | +| 07 | 郑竹 | 1989-01-01 00:00:00 | 女 | ++-----+--------+---------------------+------+ +7 rows in set (0.00 sec) +``` + +方法二: + +将量表查询使用`join`查询,一定要加`distinct`,如果不加,就会多出很多条重复数据。 + +```mysql +mysql> select distinct s.* from Student s join SC sc on s.SId=sc.SId where sc.CId in (select sc.CId from SC sc where sc.SId='01'); ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | +| 02 | 钱电 | 1990-12-21 00:00:00 | 男 | +| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | +| 04 | 李云 | 1990-12-06 00:00:00 | 男 | +| 05 | 周梅 | 1991-12-01 00:00:00 | 女 | +| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | +| 07 | 郑竹 | 1989-01-01 00:00:00 | 女 | ++-----+--------+---------------------+------+ +7 rows in set (0.00 sec) +``` + +> 查询与学号为" 01 "的同学所学完全相同的其他同学的信息 + +该查询有两个关键点: + +第一:完全相同 + +第二:其他同学 + +基于这两个关键点,我们得出查询语句需要考虑下面两种条件: + +第一:查询所学课程是否全部存在于学号01的学生的所学课程中 + +上述简化为:查询不等于学号01且课程id均包含在01所学课程中的学生id。 + +```mysql +SELECT DISTINCT sc.SId +FROM SC sc +WHERE sc.SId <> '01' + AND sc.CId IN ( + SELECT sc.CId + FROM SC sc + WHERE sc.SId = '01' + ); +``` + +第二:查询所学课程数目与学号01的学生所学课程相等。 + +上述简化为:查询 + +```mysql +SELECT DISTINCT sc.SId +FROM SC sc +WHERE sc.SId <> '01' + AND sc.CId IN ( + SELECT sc.CId + FROM SC sc + WHERE sc.SId = '01' + ) +GROUP BY sc.SId +HAVING COUNT(1) = ( + SELECT COUNT(1) + FROM SC sc + WHERE sc.SId = '01' +) +``` + +最后,得出:查询与学号为" 01 "的同学所学完全相同的其他同学的信息 + +```mysql +SELECT s.* +FROM Student s +WHERE s.SId IN ( + SELECT sc.SId + FROM SC sc + WHERE sc.SId <> '01' + AND sc.CId IN ( + SELECT sc.CId + FROM SC sc + WHERE sc.SId = '01' + ) + GROUP BY sc.SId + HAVING COUNT(1) = ( + SELECT COUNT(1) + FROM SC sc + WHERE sc.SId = '01' + ) +); +``` + +查询结果: + +```mysql ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 02 | 钱电 | 1990-12-21 00:00:00 | 男 | +| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | +| 04 | 李云 | 1990-12-06 00:00:00 | 男 | ++-----+--------+---------------------+------+ +3 rows in set (0.00 sec) + +``` + +同理,也可以用not in 来表示: + +```mysql +SELECT s.* +FROM Student s +WHERE s.SId IN ( + SELECT sc.SId + FROM SC sc + WHERE sc.SId NOT IN ( + SELECT sc.SId + FROM SC sc + WHERE sc.CId NOT IN ( + SELECT sc.CId + FROM SC sc + WHERE sc.SId = '01' + ) + ) + GROUP BY sc.SId + HAVING COUNT(1) = ( + SELECT COUNT(1) + FROM SC sc + WHERE sc.SId = '01' + ) + AND sc.SId <> '01' +); +``` + +> 查询没学过"张三"老师讲授的任一门课程的学生姓名 + +考察多层嵌套 + +```mysql +SELECT s.Sname +FROM Student s +WHERE s.SId NOT IN ( + SELECT sc.SId + FROM SC sc + WHERE sc.CId IN ( + SELECT c.CId + FROM Teacher t, Course c + WHERE t.Tname = '张三' + AND t.TId = c.TId + ) +); +``` + +查询结果: + +```mysql ++--------+ +| Sname | ++--------+ +| 吴兰 | +| 张三 | +| 李四 | +| 李四 | +| 赵六 | +| 孙七 | ++--------+ +6 rows in set (0.00 sec) +``` + +> 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩 + +```mysql +mysql> select s.SId,s.Sname,avg(sc.score) from Student s join SC sc on sc.SId=s.SId and sc.score<60 group by sc.SId having count(1)>1; ++-----+--------+---------------+ +| SId | Sname | avg(sc.score) | ++-----+--------+---------------+ +| 04 | 李云 | 33.33333 | +| 06 | 吴兰 | 32.50000 | ++-----+--------+---------------+ +2 rows in set (0.00 sec) +``` + +可以使用join中的on添加多个条件,也可以使用一个where。(注意如果是left或right join的话,就要注意了,这两种是不等价的!) + +```mysql +mysql> select s.SId,s.Sname,avg(sc.score) from Student s join SC sc on sc.SId=s.SId where sc.score<60 group by sc.SId having count(*)>1; ++-----+--------+---------------+ +| SId | Sname | avg(sc.score) | ++-----+--------+---------------+ +| 04 | 李云 | 33.33333 | +| 06 | 吴兰 | 32.50000 | ++-----+--------+---------------+ +2 rows in set (0.00 sec) +``` + +> 检索" 01 "课程分数小于 60,按分数降序排列的学生信息 + +```mysql +mysql> select s.* from Student s,SC sc where s.SId=sc.SId and sc.score<60 and sc.CId='01' order by sc.score desc; ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 04 | 李云 | 1990-12-06 00:00:00 | 男 | +| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | ++-----+--------+---------------------+------+ +2 rows in set (0.01 sec) +``` + +当加入`distinct`关键字就会报错: + +```mysql +mysql> select distinct s.* from Student s,SC sc where s.SId=sc.SId and sc.score<60 and sc.CId='01' order by sc.score desc; +ERROR 3065 (HY000): Expression #1 of ORDER BY clause is not in SELECT list, references column 'pratice.sc.score' which is not in SELECT list; this is incompatible with DISTINCT +``` + +此时解决方案:把order by的字段添加到select语句中即可! + +```mysql +mysql> select distinct s.*,sc.score from Student s,SC sc where s.SId=sc.SId and sc.score<60 and sc.CId='01' order by sc.score desc; ++-----+--------+---------------------+------+-------+ +| SId | Sname | Sage | Ssex | score | ++-----+--------+---------------------+------+-------+ +| 04 | 李云 | 1990-12-06 00:00:00 | 男 | 50.0 | +| 06 | 吴兰 | 1992-01-01 00:00:00 | 女 | 31.0 | ++-----+--------+---------------------+------+-------+ +2 rows in set (0.00 sec) +``` + +

distinct和order by一起用时,order by的字段必须在select中,不论在ORACLE或者MYSQL都是这样。
首先,distinct的执行顺序高于order by。
第二,distinct执行时会对查询的记录进行去重,产生一张虚拟的临时表;
第三,order by执行时对查询的虚拟临时表进行排序,产生新的虚拟临时表。综合来看,如果order by的字段不在select中,执行sql语句时首先执行distinct,之后产生的虚拟临时表中没有order by的字段,所以再执行order by时会报错。

+ +asd \ No newline at end of file diff --git "a/prac/\345\256\236\346\210\2303.md" "b/prac/\345\256\236\346\210\2303.md" new file mode 100644 index 0000000..7d3f5d5 --- /dev/null +++ "b/prac/\345\256\236\346\210\2303.md" @@ -0,0 +1,211 @@ +## 关于我 + +- 个人网站 + + https://light-city.club + +- 个人微信公众号 + +![wechat](../img/wechat.jpg) + +## 1.生成组内连续但不唯一的数字。类似Oracle的DENSE_RANK()函数。 + +> 按各科成绩进行排序,并显示排名, Score 重复时**合并名次空缺** + +```mysql +set @cid=0; +set @rank=0; +set @score=0; +select @rank:=IF(@cid=sc.CId,IF(@score=sc.score,@rank,@rank+1),1) '排名',@cid:=sc.CId '课程编号' ,sc.SId,@score:=sc.score score from SC sc order by cid,score desc; ++--------+--------------+-----+-------+ +| 排名 | 课程编号 | SId | score | ++--------+--------------+-----+-------+ +| 1 | 01 | 01 | 80.0 | +| 1 | 01 | 03 | 80.0 | +| 2 | 01 | 05 | 76.0 | +| 3 | 01 | 02 | 70.0 | +| 4 | 01 | 04 | 50.0 | +| 5 | 01 | 06 | 31.0 | +| 1 | 02 | 01 | 90.0 | +| 2 | 02 | 07 | 89.0 | +| 3 | 02 | 05 | 87.0 | +| 4 | 02 | 03 | 80.0 | +| 5 | 02 | 02 | 60.0 | +| 6 | 02 | 04 | 30.0 | +| 1 | 03 | 01 | 99.0 | +| 2 | 03 | 07 | 98.0 | +| 3 | 03 | 03 | 80.0 | +| 3 | 03 | 02 | 80.0 | +| 4 | 03 | 06 | 34.0 | +| 5 | 03 | 04 | 20.0 | ++--------+--------------+-----+-------+ +18 rows in set (0.00 sec) +``` + +> 查询学生的总成绩,并进行排名,总分重复时不保留名次空缺 + +```mysql +set @total=0; +select @total:=IF(@total=s.total,@total,@total+1) '排名',s.SId,s.total from (select sc.SId,sum(sc.score) total from SC sc group by sc.SId) as s order by total desc; ++--------+-----+-------+ +| 排名 | SId | total | ++--------+-----+-------+ +| 1 | 01 | 269.0 | +| 2 | 03 | 240.0 | +| 3 | 02 | 210.0 | +| 4 | 07 | 187.0 | +| 5 | 05 | 163.0 | +| 6 | 04 | 100.0 | +| 7 | 06 | 65.0 | ++--------+-----+-------+ +7 rows in set (0.00 sec) +``` + +## 2.生成组内连续且唯一的数字。类似Oracle的ROW_NUMBER()函数 + +首先初始化变量 + +```mysql +set @cid=0; +set @rank=0; +``` + +使用`IF` + +```mysql +select @rank:=IF(@cid=sc.CId,@rank+1,1) '排名',@cid:=sc.CId '课程编号' ,sc.SId,sc.score from SC sc order by cid,sc.score desc; +``` + +使用`case when end` + +```mysql +select @rank:=(case @cid when sc.CId THEN @rank+1 else 1 end) '排名',@cid:=sc.CId '课程编号' ,sc.SId,sc.score from SC sc order by cid,sc.score desc; +``` + +查询结果: + +```mysql ++--------+--------------+-----+-------+ +| 排名 | 课程编号 | SId | score | ++--------+--------------+-----+-------+ +| 1 | 01 | 01 | 80.0 | +| 2 | 01 | 03 | 80.0 | +| 3 | 01 | 05 | 76.0 | +| 4 | 01 | 02 | 70.0 | +| 5 | 01 | 04 | 50.0 | +| 6 | 01 | 06 | 31.0 | +| 1 | 02 | 01 | 90.0 | +| 2 | 02 | 07 | 89.0 | +| 3 | 02 | 05 | 87.0 | +| 4 | 02 | 03 | 80.0 | +| 5 | 02 | 02 | 60.0 | +| 6 | 02 | 04 | 30.0 | +| 1 | 03 | 01 | 99.0 | +| 2 | 03 | 07 | 98.0 | +| 3 | 03 | 03 | 80.0 | +| 4 | 03 | 02 | 80.0 | +| 5 | 03 | 06 | 34.0 | +| 6 | 03 | 04 | 20.0 | ++--------+--------------+-----+-------+ +18 rows in set (0.00 sec) +``` + +

注意:select中排名必须在课程编号前面,否则结果不正确!

+ +## 3.生成组内既不连续也不唯一的数字。类似Oracle的RANK()函数。 + +> 按各科成绩进行排序,并显示排名, Score 重复时**保留名次空缺** + +``` +select @enum:=IF(@cid=sc.CId,@enum+1,1) enum,@rank:=IF(@cid=sc.CId,IF(@score=sc.score,@rank,@enum),1) '排名',@cid:=sc.CId '课程编号' ,sc.SId,@score:=sc.score score from SC sc order by cid,score desc; +``` + +查询结果: + +```mysql +mysql> select @enum:=IF(@cid=sc.CId,@enum+1,1) enum,@rank:=IF(@cid=sc.CId,IF(@score=sc.score,@rank,@enum),1) '排名',@cid:=sc.CId '课程编号' ,sc.SId,@score:=sc.score score from SC sc order by cid,score desc; ++------+--------+--------------+-----+-------+ +| enum | 排名 | 课程编号 | SId | score | ++------+--------+--------------+-----+-------+ +| 1 | 1 | 01 | 01 | 80.0 | +| 2 | 1 | 01 | 03 | 80.0 | +| 3 | 3 | 01 | 05 | 76.0 | +| 4 | 4 | 01 | 02 | 70.0 | +| 5 | 5 | 01 | 04 | 50.0 | +| 6 | 6 | 01 | 06 | 31.0 | +| 1 | 1 | 02 | 01 | 90.0 | +| 2 | 2 | 02 | 07 | 89.0 | +| 3 | 3 | 02 | 05 | 87.0 | +| 4 | 4 | 02 | 03 | 80.0 | +| 5 | 5 | 02 | 02 | 60.0 | +| 6 | 6 | 02 | 04 | 30.0 | +| 1 | 1 | 03 | 01 | 99.0 | +| 2 | 2 | 03 | 07 | 98.0 | +| 3 | 3 | 03 | 03 | 80.0 | +| 4 | 3 | 03 | 02 | 80.0 | +| 5 | 5 | 03 | 06 | 34.0 | +| 6 | 6 | 03 | 04 | 20.0 | ++------+--------+--------------+-----+-------+ +18 rows in set (0.00 sec) +``` + +使用自连接: + +```mysql +mysql> select sc1.CId,sc1.SId,sc1.score,count(sc2.score)+1 rank from SC sc1 left join SC sc2 on sc1.CId=sc2.CId and sc1.score 不按各科成绩进行排序,并显示排名, Score 重复时保留名次空缺 + +```mysql +select sc1.CId,sc1.SId,sc1.score,(select count(1) from SC sc2 where sc2.score>sc1.score)+1 as rank from SC sc1 order by rank; ++-----+-----+-------+------+ +| CId | SId | score | rank | ++-----+-----+-------+------+ +| 03 | 01 | 99.0 | 1 | +| 03 | 07 | 98.0 | 2 | +| 02 | 01 | 90.0 | 3 | +| 02 | 07 | 89.0 | 4 | +| 02 | 05 | 87.0 | 5 | +| 01 | 01 | 80.0 | 6 | +| 02 | 03 | 80.0 | 6 | +| 03 | 03 | 80.0 | 6 | +| 03 | 02 | 80.0 | 6 | +| 01 | 03 | 80.0 | 6 | +| 01 | 05 | 76.0 | 11 | +| 01 | 02 | 70.0 | 12 | +| 02 | 02 | 60.0 | 13 | +| 01 | 04 | 50.0 | 14 | +| 03 | 06 | 34.0 | 15 | +| 01 | 06 | 31.0 | 16 | +| 02 | 04 | 30.0 | 17 | +| 03 | 04 | 20.0 | 18 | ++-----+-----+-------+------+ +18 rows in set (0.00 sec) +``` + + + diff --git "a/prac/\345\256\236\346\210\2304.md" "b/prac/\345\256\236\346\210\2304.md" new file mode 100644 index 0000000..7c4872a --- /dev/null +++ "b/prac/\345\256\236\346\210\2304.md" @@ -0,0 +1,654 @@ +## 关于我 + +- 个人网站 + + https://light-city.club + +- 个人微信公众号 + +![wechat](../img/wechat.jpg) + +> 按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩 + +考虑两点: + +第一点:平均成绩从高到低排序; + +第二点:所有学生;(要想到用join)。 + +```mysql +mysql> select * from SC sc left join (select sc.SId, avg(sc.score) AVG from SC sc group by sc.SId order by AVG desc)r on sc.SId=r.SId; ++-----+-----+-------+------+----------+ +| SId | CId | score | SId | AVG | ++-----+-----+-------+------+----------+ +| 01 | 01 | 80.0 | 01 | 89.66667 | +| 01 | 02 | 90.0 | 01 | 89.66667 | +| 01 | 03 | 99.0 | 01 | 89.66667 | +| 02 | 01 | 70.0 | 02 | 70.00000 | +| 02 | 02 | 60.0 | 02 | 70.00000 | +| 02 | 03 | 80.0 | 02 | 70.00000 | +| 03 | 01 | 80.0 | 03 | 80.00000 | +| 03 | 02 | 80.0 | 03 | 80.00000 | +| 03 | 03 | 80.0 | 03 | 80.00000 | +| 04 | 01 | 50.0 | 04 | 33.33333 | +| 04 | 02 | 30.0 | 04 | 33.33333 | +| 04 | 03 | 20.0 | 04 | 33.33333 | +| 05 | 01 | 76.0 | 05 | 81.50000 | +| 05 | 02 | 87.0 | 05 | 81.50000 | +| 06 | 01 | 31.0 | 06 | 32.50000 | +| 06 | 03 | 34.0 | 06 | 32.50000 | +| 07 | 02 | 89.0 | 07 | 93.50000 | +| 07 | 03 | 98.0 | 07 | 93.50000 | ++-----+-----+-------+------+----------+ +18 rows in set (0.00 sec) +``` + +> 查询各科成绩最高分、最低分和平均分以如下形式显示: +> +> 课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率 +> +> 及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90 +> +> 要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列 + +``` +SELECT sc.CId '课程 ID', c.Cname '课程 name', MAX(sc.score) AS '最高分' + , MIN(sc.score) AS '最低分', AVG(sc.score) AS '平均成绩' + , COUNT(sc.CId) AS '选修人数' + , SUM(CASE + WHEN sc.score >= 60 THEN 1 + ELSE 0 + END) / COUNT(sc.CId) AS '及格率' + , SUM(CASE + WHEN sc.score >= 70 + AND sc.score < 80 THEN 1 + ELSE 0 + END) / COUNT(sc.CId) AS '中等率' + , SUM(CASE + WHEN sc.score >= 80 + AND sc.score < 90 THEN 1 + ELSE 0 + END) / COUNT(sc.CId) AS '优良率' + , SUM(CASE + WHEN sc.score >= 90 THEN 1 + ELSE 0 + END) / COUNT(sc.CId) AS '优秀率' +FROM SC sc + JOIN Course c ON sc.CId = c.CId +GROUP BY sc.CId +ORDER BY COUNT(sc.CId) DESC, sc.CId ASC; +``` + +查询结果: + +```mysql ++-----------+-------------+-----------+-----------+--------------+--------------+-----------+-----------+-----------+-----------+ +| 课程 ID | 课程 name | 最高分 | 最低分 | 平均成绩 | 选修人数 | 及格率 | 中等率 | 优良率 | 优秀率 | ++-----------+-------------+-----------+-----------+--------------+--------------+-----------+-----------+-----------+-----------+ +| 01 | 语文 | 80.0 | 31.0 | 64.50000 | 6 | 0.6667 | 0.3333 | 0.3333 | 0.0000 | +| 02 | 数学 | 90.0 | 30.0 | 72.66667 | 6 | 0.8333 | 0.0000 | 0.5000 | 0.1667 | +| 03 | 英语 | 99.0 | 20.0 | 68.50000 | 6 | 0.6667 | 0.0000 | 0.3333 | 0.3333 | ++-----------+-------------+-----------+-----------+--------------+--------------+-----------+-----------+-----------+-----------+ +3 rows in set (0.00 sec) +``` + +>统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[60-0] 及所占百分比 + +```mysql +SELECT Course.Cname, Course.CId, SUM(CASE + WHEN sc.score <= 100 + AND sc.score > 85 THEN 1 + ELSE 0 + END) AS "[100-85]" + , SUM(CASE + WHEN sc.score <= 85 + AND sc.score > 70 THEN 1 + ELSE 0 + END) AS "[85-70]", SUM(CASE + WHEN sc.score <= 70 + AND sc.score > 60 THEN 1 + ELSE 0 + END) AS "[70-60]" + , SUM(CASE + WHEN sc.score <= 60 + AND sc.score > 0 THEN 1 + ELSE 0 + END) AS "[60-0]" + , SUM(CASE + WHEN sc.score <= 100 + AND sc.score > 85 THEN 1 + ELSE 0 + END) / COUNT(1) AS "[100-85]百分比" + , SUM(CASE + WHEN sc.score <= 85 + AND sc.score > 70 THEN 1 + ELSE 0 + END) / COUNT(1) AS "[85-70]百分比" + , SUM(CASE + WHEN sc.score <= 70 + AND sc.score > 60 THEN 1 + ELSE 0 + END) / COUNT(1) AS "[70-60]百分比" + , SUM(CASE + WHEN sc.score <= 60 + AND sc.score > 0 THEN 1 + ELSE 0 + END) / COUNT(1) AS "[60-0]百分比" +FROM SC sc + LEFT JOIN Course ON sc.CId = Course.CId +GROUP BY sc.CId; +``` + +> 查询各科成绩前三名的记录 + +```mysql +mysql> select * from SC sc + -> where (select count(*) from SC as a + -> where sc.CId= a.CId and sc.score order by CId asc, sc.score desc; ++-----+-----+-------+ +| SId | CId | score | ++-----+-----+-------+ +| 01 | 01 | 80.0 | +| 03 | 01 | 80.0 | +| 05 | 01 | 76.0 | +| 01 | 02 | 90.0 | +| 07 | 02 | 89.0 | +| 05 | 02 | 87.0 | +| 01 | 03 | 99.0 | +| 07 | 03 | 98.0 | +| 02 | 03 | 80.0 | +| 03 | 03 | 80.0 | ++-----+-----+-------+ +10 rows in set (0.00 sec) +``` + +> 查询每门课程被选修的学生数 + +```mysql +mysql> select CId,count(SId) from SC group by CId; ++-----+------------+ +| CId | count(SId) | ++-----+------------+ +| 01 | 6 | +| 02 | 6 | +| 03 | 6 | ++-----+------------+ +3 rows in set (0.00 sec) +``` + +> 查询出只选修两门课程的学生学号和姓名 + +联合查询:`join`连接: + +```mysql +mysql> select s.SId,s.Sname from Student s join SC sc on s.SId=sc.SId group by sc.SId having count(1)=2; ++-----+--------+ +| SId | Sname | ++-----+--------+ +| 05 | 周梅 | +| 06 | 吴兰 | +| 07 | 郑竹 | ++-----+--------+ +3 rows in set (0.00 sec) +``` + +嵌套查询:`in`子句 + +```mysql +mysql> select s.SId,s.Sname from Student s where s.SId in (select sc.SId from SC sc group by sc.SId having count(1)=2); ++-----+--------+ +| SId | Sname | ++-----+--------+ +| 05 | 周梅 | +| 06 | 吴兰 | +| 07 | 郑竹 | ++-----+--------+ +3 rows in set (0.00 sec) +``` + +> 查询男生、女生人数 + +```mysql +mysql> select s.Ssex,count(s.Ssex) as '总人数' from Student s group by s.Ssex; ++------+-----------+ +| Ssex | 总人数 | ++------+-----------+ +| 男 | 4 | +| 女 | 8 | ++------+-----------+ +2 rows in set (0.00 sec) +``` + +> 查询名字中含有「风」字的学生信息 + +```mysql +mysql> select * from Student where Sname like '%风%'; ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | ++-----+--------+---------------------+------+ +1 row in set (0.00 sec) +``` + +> 查询同名学生名单,并统计同名人数 + +```mysql +mysql> select s.Sname,count(*) as '同名人数' from Student s group by s.Sname having count(*)>1; ++--------+--------------+ +| Sname | 同名人数 | ++--------+--------------+ +| 李四 | 2 | ++--------+--------------+ +1 row in set (0.00 sec) +``` + +> 查询1990年出生的学生名单 + +`like`通配符: + +```mysql +mysql> select * from Student where Sage like '1990-%'; ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | +| 02 | 钱电 | 1990-12-21 00:00:00 | 男 | +| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | +| 04 | 李云 | 1990-12-06 00:00:00 | 男 | ++-----+--------+---------------------+------+ +4 rows in set, 1 warning (0.00 sec) +``` + +`year`函数: + +```mysql +mysql> select * from Student where year(Sage)=1990; ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | +| 02 | 钱电 | 1990-12-21 00:00:00 | 男 | +| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | +| 04 | 李云 | 1990-12-06 00:00:00 | 男 | ++-----+--------+---------------------+------+ +4 rows in set (0.00 sec) +``` + +> 查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列 + +```mysql +mysql> select sc.CId,avg(sc.score) as '平均成绩' from SC sc group by sc.CId order by 平均成绩 desc ,sc.CId asc; ++-----+--------------+ +| CId | 平均成绩 | ++-----+--------------+ +| 02 | 72.66667 | +| 03 | 68.50000 | +| 01 | 64.50000 | ++-----+--------------+ +3 rows in set (0.00 sec) +``` + +> 查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩 + +```mysql +mysql> select s.SId,s.Sname,avg(sc.score) as average from Student s left join SC sc on sc.SId=s.SId group by s.SId having average>=85; ++-----+--------+----------+ +| SId | Sname | average | ++-----+--------+----------+ +| 01 | 赵雷 | 89.66667 | +| 07 | 郑竹 | 93.50000 | ++-----+--------+----------+ +2 rows in set (0.00 sec) +``` + +> 查询课程名称为「数学」,且分数低于 60 的学生姓名和分数 + +```mysql +mysql> select s.Sname,sc.score from Course c,SC sc,Student s where c.Cname='数学' and c.CId=sc.CId and sc.score<60 and sc.SId=s.SId; ++--------+-------+ +| Sname | score | ++--------+-------+ +| 李云 | 30.0 | ++--------+-------+ +1 row in set (0.00 sec) +``` + +> 查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况) + +```mysql +mysql> select s.Sname,sc.CId,sc.score from Student s left join SC sc on s.SId=sc.SId; ++--------+------+-------+ +| Sname | CId | score | ++--------+------+-------+ +| 赵雷 | 01 | 80.0 | +| 赵雷 | 02 | 90.0 | +| 赵雷 | 03 | 99.0 | +| 钱电 | 01 | 70.0 | +| 钱电 | 02 | 60.0 | +| 钱电 | 03 | 80.0 | +| 孙风 | 01 | 80.0 | +| 孙风 | 02 | 80.0 | +| 孙风 | 03 | 80.0 | +| 李云 | 01 | 50.0 | +| 李云 | 02 | 30.0 | +| 李云 | 03 | 20.0 | +| 周梅 | 01 | 76.0 | +| 周梅 | 02 | 87.0 | +| 吴兰 | 01 | 31.0 | +| 吴兰 | 03 | 34.0 | +| 郑竹 | 02 | 89.0 | +| 郑竹 | 03 | 98.0 | +| 张三 | NULL | NULL | +| 李四 | NULL | NULL | +| 李四 | NULL | NULL | +| 赵六 | NULL | NULL | +| 孙七 | NULL | NULL | ++--------+------+-------+ +23 rows in set (0.00 sec) +``` + +> 查询任何一门课程成绩在 70 分以上的姓名、课程名称和分数 + +```mysql +mysql> select s.Sname, c.Cname,sc.score from Student s,Course c,SC sc + -> where sc.score>70 + -> and s.SId= sc.SId + -> and sc.CId= c.CId; ++--------+--------+-------+ +| Sname | Cname | score | ++--------+--------+-------+ +| 赵雷 | 语文 | 80.0 | +| 赵雷 | 数学 | 90.0 | +| 赵雷 | 英语 | 99.0 | +| 钱电 | 英语 | 80.0 | +| 孙风 | 语文 | 80.0 | +| 孙风 | 数学 | 80.0 | +| 孙风 | 英语 | 80.0 | +| 周梅 | 语文 | 76.0 | +| 周梅 | 数学 | 87.0 | +| 郑竹 | 数学 | 89.0 | +| 郑竹 | 英语 | 98.0 | ++--------+--------+-------+ +11 rows in set (0.00 sec) +``` + +> 查询存在不及格的课程 + +`group by`取唯一: + +```mysql +mysql> select sc.CId from SC sc where sc.score<60 group by sc.CId; ++-----+ +| CId | ++-----+ +| 01 | +| 02 | +| 03 | ++-----+ +3 rows in set (0.00 sec) +``` + +`distinct`取唯一: + +```mysql +mysql> select distinct sc.CId from SC sc where sc.score<60; ++-----+ +| CId | ++-----+ +| 01 | +| 02 | +| 03 | ++-----+ +3 rows in set (0.00 sec) +``` + +> 查询课程编号为 01 且课程成绩在 80 分及以上的学生的学号和姓名 + +```mysql +mysql> select s.SId,s.Sname from Student s,SC sc where sc.SId=s.SId and sc.score>=80 and sc.CId='01'; ++-----+--------+ +| SId | Sname | ++-----+--------+ +| 01 | 赵雷 | +| 03 | 孙风 | ++-----+--------+ +2 rows in set (0.00 sec) +``` + +> 求每门课程的学生人数 + +```mysql +mysql> select sc.CId,count(*) from SC sc group by sc.CId; ++-----+----------+ +| CId | count(*) | ++-----+----------+ +| 01 | 6 | +| 02 | 6 | +| 03 | 6 | ++-----+----------+ +3 rows in set (0.00 sec) +``` + +>查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩 + +下面两种方法不管是成绩重复与否,都可以查询出来! + +`in`子查询: + +```mysql +mysql> select * from Student s,SC sc where s.SId=sc.SId and sc.score =( select max(sc.score) from SC sc,Teacher t,Course c where c.TId=t.TId and c.CId=sc.CId and t.Tname='张三'); ++-----+--------+---------------------+------+-----+-----+-------+ +| SId | Sname | Sage | Ssex | SId | CId | score | ++-----+--------+---------------------+------+-----+-----+-------+ +| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | 01 | 02 | 90.0 | ++-----+--------+---------------------+------+-----+-----+-------+ +1 row in set (0.00 sec) +``` + +`limit`查询 + +```mysql +mysql> select s.*,sc.score from Student s,SC sc,Teacher t,Course c where c.TId=t.TId and c.CId=sc.CId and sc.SId=s.SId and t.Tname='张三' order by sc.score desc limit 1; ++-----+--------+---------------------+------+-------+ +| SId | Sname | Sage | Ssex | score | ++-----+--------+---------------------+------+-------+ +| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | 90.0 | ++-----+--------+---------------------+------+-------+ +1 row in set (0.01 sec) +``` + +> 查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩 + +```mysql +mysql> select distinct sc1.CId,sc1.SId,sc1.score from SC sc1 join SC sc2 on sc1.SId=sc2.SId and sc1.CId!=sc2.CId and sc1.score=sc2.score; ++-----+-----+-------+ +| CId | SId | score | ++-----+-----+-------+ +| 02 | 03 | 80.0 | +| 03 | 03 | 80.0 | +| 01 | 03 | 80.0 | ++-----+-----+-------+ +3 rows in set (0.00 sec) +``` + +> 查询每门功成绩最好的前两名 + +嵌套子查询: + +```mysql +mysql> select * from SC sc + -> where (select count(*) from SC as a + -> where sc.CId= a.CId and sc.score order by CId asc, sc.score desc; ++-----+-----+-------+ +| SId | CId | score | ++-----+-----+-------+ +| 01 | 01 | 80.0 | +| 03 | 01 | 80.0 | +| 01 | 02 | 90.0 | +| 07 | 02 | 89.0 | +| 01 | 03 | 99.0 | +| 07 | 03 | 98.0 | ++-----+-----+-------+ +6 rows in set (0.01 sec) +``` + +`group by having` + +```mysql +mysql> select a.SId,a.CId,a.score from SC as a left join SC as b on a.CId=b.CId and a.score 统计每门课程的学生选修人数(超过 5 人的课程才统计) + +```mysql +mysql> select sc.CId,count(sc.SId) as total from SC sc group by sc.CId having total>5; ++-----+-------+ +| CId | total | ++-----+-------+ +| 01 | 6 | +| 02 | 6 | +| 03 | 6 | ++-----+-------+ +3 rows in set (0.00 sec) +``` + +> 检索至少选修两门课程的学生学号 + +```mysql +mysql> select sc.SId,count(sc.CId) as count from SC sc group by sc.SId having count(sc.CId)>=2; ++-----+-------+ +| SId | count | ++-----+-------+ +| 01 | 3 | +| 02 | 3 | +| 03 | 3 | +| 04 | 3 | +| 05 | 2 | +| 06 | 2 | +| 07 | 2 | ++-----+-------+ +7 rows in set (0.00 sec) +``` + +> 查询选修了全部课程的学生信息 + +```mysql +mysql> select s.* from Student s,SC sc where sc.SId=s.SId group by sc.SId having count(sc.CId)=(select distinct count(*) from Course); ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | +| 02 | 钱电 | 1990-12-21 00:00:00 | 男 | +| 03 | 孙风 | 1990-12-20 00:00:00 | 男 | +| 04 | 李云 | 1990-12-06 00:00:00 | 男 | ++-----+--------+---------------------+------+ +4 rows in set (0.00 sec) +``` + +> 查询各学生的年龄,只按年份来算 + +```mysql +mysql> select s.Sname,year(curdate())-year(s.Sage) as 年龄 from Student s; ++--------+--------+ +| Sname | 年龄 | ++--------+--------+ +| 赵雷 | 29 | +| 钱电 | 29 | +| 孙风 | 29 | +| 李云 | 29 | +| 周梅 | 28 | +| 吴兰 | 27 | +| 郑竹 | 30 | +| 张三 | 2 | +| 李四 | 2 | +| 李四 | 7 | +| 赵六 | 6 | +| 孙七 | 5 | ++--------+--------+ +12 rows in set (0.00 sec) +``` + +> 按照出生日期来算,当前月日 < 出生年月的月日,则年龄减一 + +```mysql +mysql> select s.SId,s.Sname,( case + -> when MONTH(curdate()) when MONTH(s.Sage)=month(curdate()) and DAYOfmonth(s.Sage) then year(curdate())-year(s.Sage)-1 + -> else year(curdate())-year(s.Sage) + -> end) as 年龄 from Student s; ++-----+--------+--------+ +| SId | Sname | 年龄 | ++-----+--------+--------+ +| 01 | 赵雷 | 29 | +| 02 | 钱电 | 28 | +| 03 | 孙风 | 28 | +| 04 | 李云 | 28 | +| 05 | 周梅 | 27 | +| 06 | 吴兰 | 27 | +| 07 | 郑竹 | 30 | +| 09 | 张三 | 1 | +| 10 | 李四 | 1 | +| 11 | 李四 | 6 | +| 12 | 赵六 | 5 | +| 13 | 孙七 | 4 | ++-----+--------+--------+ +12 rows in set (0.00 sec) +``` + +> 查询本周过生日的学生 + +`WEEKOFYEAR(date)`返回日期用数字表示的范围是从1到53的日历周。` + +```mysql +mysql> select * from Student s where weekofyear(s.Sage)=weekofyear(curdate()); +Empty set (0.00 sec) +``` + +>查询下周过生日的学生 + +```mysql +mysql> select * + -> from Student s + -> where WEEKOFYEAR(s.Sage)=WEEKOFYEAR(CURDATE())+1; +Empty set (0.00 sec) +``` + +> 查询本月过生日的学生 + +```mysql +mysql> select * + -> from Student s + -> where MONTH(s.Sage)=MONTH(CURDATE()); ++-----+--------+---------------------+------+ +| SId | Sname | Sage | Ssex | ++-----+--------+---------------------+------+ +| 11 | 李四 | 2012-06-06 00:00:00 | 女 | +| 12 | 赵六 | 2013-06-13 00:00:00 | 女 | +| 13 | 孙七 | 2014-06-01 00:00:00 | 女 | ++-----+--------+---------------------+------+ +3 rows in set (0.00 sec) +``` + +> 查询下月过生日的学生 + +```mysql +mysql> select * + -> from Student s + -> where MONTH(s.Sage)=MONTH(CURDATE())+1; +Empty set (0.01 sec) +``` + diff --git "a/prac/\345\256\236\346\210\2305.md" "b/prac/\345\256\236\346\210\2305.md" new file mode 100644 index 0000000..eba2a1a --- /dev/null +++ "b/prac/\345\256\236\346\210\2305.md" @@ -0,0 +1,139 @@ +# 关系代数 + +## 关于我 + +- 个人网站 + + https://light-city.club + +- 个人微信公众号 + +![wechat](../img/wechat.jpg) + +## 1.Relax + +> 使用地址:http://dbis-uibk.github.io/relax/calc.htm# + +这是一个sql与关系代数练习网站!非常好用,下面来看使用! + +如下图所示:支持sql与关系代数语法! + +![](../img/sql.png) + + +选择数据集: + +![](../img/dataset.png) + +输入查询语句:(ps 看图发现自动生成关系代数,强不强大!) + +![](../img/use.png) + + +输入关系代数: + + +![](../img/ra.png) + + + +## 2.实战题目: + +用关系代数描述以下查询要求: +查询所有老师和学生的姓名 +查询Kim老师的办公地点 +查询CS系学生的学分情况 +查询database课程成绩在90分以上的学生信息 +查询没有选修任何课程的学生信息 + +选用数据库为Siberschatz-UniversityDB: + +关键表解释: + +``` +instructor(ID, name, dept_name, salary) +deptment(dept_name, building, budget) +student(ID, name, dept_name, tot_cred) +course(course_id, title, dept_name, credts) +takes(ID, course_id, semester, year, grade) + +教师信息instructor:教师号ID,教师名字name,教师所在的系dept_name,薪资salary +建筑department:系名dept_name, 建筑building, 预算budget +学生信息student:学生学号ID,学生姓名name,学生所在系dept_name,总学分tot_cred +课程course:课程号course_id,课程名称title,课程所属的系名dept_name,学分credits +选课信息takes:学生学号ID,课程标识course_id,课程段标识sec_id,学期semester,年份year,成绩grade +``` + + + +> 找出所有老师与学生的名字 + +所有老师: + +```mysql +select distinct name from instructor; +``` + +对应的关系代数: + +``` +π name (instructor) +``` + +所有学生: + +```mysql +select distinct name from student; +``` + +对应的关系代数: + +``` +π name (student) +``` + +> 查询Kim老师的办公地点 + +```mysql +select b.building from instructor as a, department as b where a.name='Kim' and a.dept_name=b.dept_name; +``` + +对应的关系代数: + +```mysql +π b.building (σ a.name = 'Kim' and a.dept_name = b.dept_name ((ρ a instructor) ⨯ (ρ b department))) +``` + +先将`department`与`instructor`进行笛卡尔积,然后选择Kim老师的数据,再对`building`进行投影,得到办公地点。 + +> 查询CS系学生的学分情况 + +``` +select tot_cred from student where dept_name='Comp. Sci.'; +``` + +对应的关系代数: + +``` +π ID, name, tot_cred σ dept_name = 'Comp. Sci.' (student) +``` + +> 查询database课程成绩在90分以上(等级为A)的学生信息 + +```mysql +select s.*,t.grade from student as s,course as c,takes as t where s.dept_name=c.dept_name and t.course_id=c.course_id and c.title='Database System Concepts' and t.grade='A'; +``` + +对应的关系代数: + +``` +π s.ID,s.name,s.dept_name,s.tot_cred σ c.title='Database System Concepts' and t.grade='A' and c.dept_name=s.dept_name and c.course_id=t.course_id (ρ c course ⨯ ρ t takes ⨯ ρ s student) +``` + +> 查询没有选修任何课程的学生信息 + +``` +π b.ID,b.name,b.dept_name,b.tot_cred σ a.ID=b.ID ((ρ a ((π ID student) - (π ID takes))) ⨯ (ρ b (student))) +``` + +先选出所有的学生ID再减去选课表中的学生ID,然后根据ID去检索学生表的信息,就是没有选修任何课程的学生信息。 \ No newline at end of file diff --git "a/prac/\345\256\236\346\210\2306.md" "b/prac/\345\256\236\346\210\2306.md" new file mode 100644 index 0000000..99b3011 --- /dev/null +++ "b/prac/\345\256\236\346\210\2306.md" @@ -0,0 +1,380 @@ +## 关于我 + +- 个人网站 + + https://light-city.club + +- 个人微信公众号 + +![wechat](../img/wechat.jpg) + +## 1.笛卡尔积、等值联接、自然连接三者之间区别 + +笛卡尔积对两个关系R和S进行操作,产生的关系中元组个数为两个关系中元组个数之积。等值联接则是在笛卡尔积的结果上再进行选择操作,挑选关系第i个分量与第(r+j)个分量值相等的元组;自然连接则是在等值联接(以公共属性值相等为条件)的基础上再行投影操作,去掉S中的公共属性列,当两个关系没有公共属性时,自然连接就转化成笛卡尔积。 + +1)等值连接必须要有等值的条件,当条件不同时连接的结果也不相同,两个关系可以没有相同的属性列 + +2)自然连接必须要有相同的属性列才能进行 + +下面演示上述理论: + +现有S与R两张表,数据如下: + +```mysql +mysql> select * from S; ++------+------+ +| A | B | ++------+------+ +| 1 | 2 | +| 3 | 3 | +| 5 | 9 | ++------+------+ +3 rows in set (0.00 sec) + +mysql> select * from R; ++------+------+ +| B | C | ++------+------+ +| 2 | 1 | +| 7 | 2 | +| 3 | 5 | ++------+------+ +3 rows in set (0.00 sec) +``` + +- 笛卡尔积操作 + +```mysql +mysql> select * from S,R; ++------+------+------+------+ +| A | B | B | C | ++------+------+------+------+ +| 1 | 2 | 2 | 1 | +| 3 | 3 | 2 | 1 | +| 5 | 9 | 2 | 1 | +| 1 | 2 | 7 | 2 | +| 3 | 3 | 7 | 2 | +| 5 | 9 | 7 | 2 | +| 1 | 2 | 3 | 5 | +| 3 | 3 | 3 | 5 | +| 5 | 9 | 3 | 5 | ++------+------+------+------+ +9 rows in set (0.00 sec) +``` + +- 等值连接 + +没有相同属性列的S.A=R.C等值连接: + +```mysql +mysql> select * from S,R where S.A=R.C; ++------+------+------+------+ +| A | B | B | C | ++------+------+------+------+ +| 1 | 2 | 2 | 1 | +| 5 | 9 | 3 | 5 | ++------+------+------+------+ +2 rows in set (0.00 sec) +``` + +有相同属性列的S.B=R.B等值连接: + +```mysql +mysql> select * from S,R where S.B=R.B; ++------+------+------+------+ +| A | B | B | C | ++------+------+------+------+ +| 1 | 2 | 2 | 1 | +| 3 | 3 | 3 | 5 | ++------+------+------+------+ +2 rows in set (0.00 sec) +``` + +- 自然连接 + +```mysql +mysql> select * from S,R where S.B=R.B; ++------+------+------+------+ +| A | B | B | C | ++------+------+------+------+ +| 1 | 2 | 2 | 1 | +| 3 | 3 | 3 | 5 | ++------+------+------+------+ +2 rows in set (0.00 sec) +``` + +## 2.关系代数 + +进入http://dbis-uibk.github.io/relax/calc.htm#网站,选择Wikipedia - Relational algebra (en)数据库进行练习! + +查询练习应包含π σ ÷ ⨝ ⟕ 等运算符。 + +### 2.1 字段描述 + +```mysql +Employee 雇员 + Name string + EmpId number + DeptName string +Dept 部门 + DeptName string + Manager string +Completed 已完成 + Student string + Task string + DBProject + Task string +Car 汽车 + CarModel string + CarPrice number +Boat 船 + BoatModel string + BoatPrice number +``` + +### 2.2关系代数 + +- 自然连接练习 + +``` +π Name, DeptName σ DeptName = 'Sales' (Employee ⨝ Dept) +``` + +- 除练习 + +Completed表 + +| Completed.Student | Completed.Task | +| ----------------- | -------------- | +| Fred | Database1 | +| Fred | Database2 | +| Fred | Compiler1 | +| Eugene | Database1 | +| Eugene | Compiler1 | +| Sarah | Database1 | +| Sarah | Database2 | + +DBProject表: + +| DBProject.Task | +| -------------- | +| Database1 | +| Database2 | + +( Completed ÷ DBProject ) 结果: + +| Completed.Student | +| ----------------- | +| Fred | +| Sarah | + +Employee表: + +| Employee.Name | Employee.EmpId | Employee.DeptName | +| ------------- | -------------- | ----------------- | +| Harry | 3415 | Finance | +| Sally | 2241 | Sales | +| George | 3401 | Finance | +| Harriet | 2202 | Sales | +| Tim | 1123 | Executive | + +自然连接: + +(Completed ÷ DBProject) ⨝ Employee + +| Completed.Student | Employee.Name | Employee.EmpId | Employee.DeptName | +| ----------------- | ------------- | -------------- | ----------------- | +| Fred | Harry | 3415 | Finance | +| Fred | Sally | 2241 | Sales | +| Fred | George | 3401 | Finance | +| Fred | Harriet | 2202 | Sales | +| Fred | Tim | 1123 | Executive | +| Sarah | Harry | 3415 | Finance | +| Sarah | Sally | 2241 | Sales | +| Sarah | George | 3401 | Finance | +| Sarah | Harriet | 2202 | Sales | +| Sarah | Tim | 1123 | Executive | + +选择结果: + +| Employee.Name | Employee.DeptName | +| ------------- | ----------------- | +| Harry | Finance | +| Sally | Sales | +| George | Finance | +| Harriet | Sales | +| Tim | Executive | + +综上的关系代数为: + +``` +π Name, DeptName ( ( Completed ÷ DBProject ) ⨝ Employee ) +``` + +- 并练习 + +( Completed ÷ DBProject ) 结果: + +| Completed.Student | +| ----------------- | +| Fred | +| Sarah | + +Employee表投影出Name列结果: + +| Employee.Name | +| ------------- | +| Harry | +| Sally | +| George | +| Harriet | +| Tim | + +并: + +``` +π Name (Employee) ∪ (Completed ÷ DBProject) +``` + +| Employee.Name | +| ------------- | +| Harry | +| Sally | +| George | +| Harriet | +| Tim | +| Fred | +| Sarah | + +- 差练习 + +对Employee投影出DeptName: + +``` +π DeptName (Employee) +``` + +| Employee.DeptName | +| ----------------- | +| Finance | +| Sales | +| Executive | + +对Dept投影出DeptName: + +``` +π DeptName (Dept) +``` + +| Dept.DeptName | +| ------------- | +| Sales | +| Production | + +两张表的DeptName做差运算: + +``` +π DeptName (Employee) - π DeptName (Dept) +``` + +| Employee.DeptName | +| ----------------- | +| Finance | +| Executive | + +- 左外连接 + +Employee表: + +| Employee.Name | Employee.EmpId | Employee.DeptName | +| ------------- | -------------- | ----------------- | +| Harry | 3415 | Finance | +| Sally | 2241 | Sales | +| George | 3401 | Finance | +| Harriet | 2202 | Sales | +| Tim | 1123 | Executive | + +Dept表: + +| Dept.DeptName | Dept.Manager | +| ------------- | ------------ | +| Sales | Harriet | +| Production | Charles | + +Employee left join Dept结果: + +``` +Employee ⟕ Dept +``` + +| Employee.Name | Employee.EmpId | Employee.DeptName | Dept.Manager | +| ------------- | -------------- | ----------------- | ------------ | +| Harry | 3415 | Finance | *null* | +| Sally | 2241 | Sales | Harriet | +| George | 3401 | Finance | *null* | +| Harriet | 2202 | Sales | Harriet | +| Tim | 1123 | Executive | *null* | + +投影选择列: + +``` +π Name, DeptName, Manager (Employee ⟕ Dept) +``` + +| Employee.Name | Employee.DeptName | Dept.Manager | +| ------------- | ----------------- | ------------ | +| Harry | Finance | *null* | +| Sally | Sales | Harriet | +| George | Finance | *null* | +| Harriet | Sales | Harriet | +| Tim | Executive | *null* | + +- 选择练习 + +Car表: + +| Car.CarModel | Car.CarPrice | +| ------------ | ------------ | +| CarA | 20000 | +| CarB | 30000 | +| CarC | 50000 | + +Boat表: + +| Boat.BoatModel | Boat.BoatPrice | +| -------------- | -------------- | +| Boat1 | 10000 | +| Boat2 | 40000 | +| Boat3 | 60000 | + +自然连接: + +``` +Car ⨝ Boat +``` + +| Car.CarModel | Car.CarPrice | Boat.BoatModel | Boat.BoatPrice | +| ------------ | ------------ | -------------- | -------------- | +| CarA | 20000 | Boat1 | 10000 | +| CarA | 20000 | Boat2 | 40000 | +| CarA | 20000 | Boat3 | 60000 | +| CarB | 30000 | Boat1 | 10000 | +| CarB | 30000 | Boat2 | 40000 | +| CarB | 30000 | Boat3 | 60000 | +| CarC | 50000 | Boat1 | 10000 | +| CarC | 50000 | Boat2 | 40000 | +| CarC | 50000 | Boat3 | 60000 | + +选择: + +``` +σ CarPrice > BoatPrice (Car ⨝ Boat) +``` + +| Car.CarModel | Car.CarPrice | Boat.BoatModel | Boat.BoatPrice | +| ------------ | ------------ | -------------- | -------------- | +| CarA | 20000 | Boat1 | 10000 | +| CarB | 30000 | Boat1 | 10000 | +| CarC | 50000 | Boat1 | 10000 | +| CarC | 50000 | Boat2 | 40000 | + diff --git "a/prac/\345\256\236\346\210\2307.md" "b/prac/\345\256\236\346\210\2307.md" new file mode 100644 index 0000000..b5ddcc9 --- /dev/null +++ "b/prac/\345\256\236\346\210\2307.md" @@ -0,0 +1,463 @@ +## 关于我 + +- 个人网站 + + https://light-city.club + +- 个人微信公众号 + +![wechat](../img/wechat.jpg) + +## 1.标准的SQL语句 + +DML(Data Manipulation Language,数据操作语言) 语句:主要由select、insert、update和 delete 四个关键字完成。 + +DDL(Data Definition Language,数据定义语言)语句:主要由create、alter、drop和 truncate 四个关键字完成。 + +DCL(Data Control Language,数据控制语言)语句:主要grant和revoke 两个关键字完成。 + +事务控制语句:主要由commit、rollback和savepoint 三个关键字完成。 + +## 2.DDL + +### 2.1 check约束无效,如何解决? + +定义学生表,练习check、primary key 、enum。 + +```mysql +CREATE TABLE Student ( + SId varchar(7), + Sname varchar(10) NOT NULL, + Ssex varchar(4) CHECK (Ssex = '男' + OR Ssex = '女'), + Sage int CHECK (Sage >= 15 + AND Sage <= 45), + Sdept varchar(20) DEFAULT '计算机系', + PRIMARY KEY (SId) +); +``` + +当往里面插入数据的时候,发现`check`约束不起作用!如下所示: + +```mysql +mysql> insert into Student values('01','赵雷','男1',70,'1'); +Query OK, 1 row affected (0.10 sec) + +mysql> select * from Student; ++-----+--------+------+------+-------+ +| SId | Sname | Ssex | Sage | Sdept | ++-----+--------+------+------+-------+ +| 01 | 赵雷 | 男1 | 70 | 1 | ++-----+--------+------+------+-------+ +1 row in set (0.00 sec) +``` + +此时查看表定义: + +```mysql +show create table Student +``` + +果不其然,没得check约束:mysql是不支持check约束的。如果你创建表的时候加上了check约束也是不起作用的。所以,你不用更改或删除之前的check约束。 + +```mysql +| Student | CREATE TABLE `Student` ( + `SId` varchar(7) NOT NULL, + `Sname` varchar(10) NOT NULL, + `Ssex` varchar(4) DEFAULT NULL, + `Sage` int(11) DEFAULT NULL, + `Sdept` varchar(20) DEFAULT '计算机系', + PRIMARY KEY (`SId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 | +``` + +#### 使用enum限制插入的值 + +所以为了完成性别这种的离散范围,可以使用`enum`,此时我们对上述表进行修改: + +```mysql +mysql> alter table Student modify column Ssex enum('男','女'); +ERROR 1265 (01000): Data truncated for column 'Ssex' at row 1 +``` + +上面提示,需要truncate data,由于之前插入的数据不符合要求,所以必须先: + +```mysql +mysql> truncate table Student; +Query OK, 0 rows affected (0.25 sec) +``` + +再进行修改: + +```mysql +mysql> alter table Student modify column Ssex enum('男','女'); +Query OK, 0 rows affected (0.62 sec) +Records: 0 Duplicates: 0 Warnings: 0 +mysql> insert into Student values('01','赵雷','男1',70,'1'); +ERROR 1265 (01000): Data truncated for column 'Ssex' at row 1 +mysql> insert into Student values('01','赵雷','男',70,'1'); +Query OK, 1 row affected (0.18 sec) +``` + +此时,再次插入错误数据,就会提示不满足列要求。 + +现在来验证一下这个表结构是否修改: + +```mysql +mysql> show create table Student; +| Student | CREATE TABLE `Student` ( + `SId` varchar(7) NOT NULL, + `Sname` varchar(10) NOT NULL, + `Ssex` enum('男','女') DEFAULT NULL, + `Sage` int(11) DEFAULT NULL, + `Sdept` varchar(20) DEFAULT '计算机系', + PRIMARY KEY (`SId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 | +``` + +会发现,enum已经有了!表结构修改成功! + +但是问题又来了,上述方法对于离散数据没问题,可是对于范围的连续数据呢?比如上述的年龄字段,本来想约束限制为15到45的,结果发现70竟然可以插入,这肯定不符合要求! + +那么如何解决呢,下面一起来看! + +#### 触发器解决限制范围内数据 + +创建触发器: + +```mysql +delimiter $$ +create trigger age_check before insert on Student +for each row +begin + declare msg varchar(100); + if new.Sage<15 or new.Sage>45 then set msg=concat('您输入的年龄值:',new.Sage,'为无效的年龄,请输入15到45以内的有效数字。'); + signal sqlstate 'HY000' set message_text=msg; +end if; +end; +$$ +delimiter ; +``` + +delimiter解释:其实就是告诉mysql解释器,该段命令是否已经结束了,mysql是否可以执行了。默认情况下,delimiter是分号(**;)** 。但有时候,不希望MySQL这么做。在为可能输入较多的语句,且语句中包含有分号。 + +上述就是先修改语句执行当碰到$$时候再去执行,然后复原;号。 + +```mysql +mysql> insert into Student values('02','赵雷','男',70,'1'); +ERROR 1644 (HY000): 您输入的年龄值:70为无效的年龄,请输入15到45以内的有效数字。 +mysql> insert into Student values('02','赵雷','男',20,'1'); +Query OK, 1 row affected (0.10 sec) +``` + +第一次插入不符合要求的年龄,发现确实提示报错了!证明check约束起作用了,当插入范围内数据,可以看到插入成功! + +### 2.2 外键 + +#### 2.2.1 创建外键 + +#### (1)不带别名的外键,数据库自动生成 + +首先创建`department`表: + +```mysql +CREATE TABLE department ( + dept_name varchar(20) PRIMARY KEY NOT NULL, + building varchar(20), + budget int +); +``` + +其次,创建`instructor`表: + +```mysql +CREATE TABLE instructor ( + ID char(5), + name varchar(20) NOT NULL, + dept_name varchar(20), + salary numeric(8, 2), + PRIMARY KEY (ID), + FOREIGN KEY (dept_name) REFERENCES department (dept_name) +); +``` + +查看生成的外键名: + +```mysql +show create table instructor + +| instructor | CREATE TABLE `instructor` ( + `ID` char(5) NOT NULL, + `name` varchar(20) NOT NULL, + `dept_name` varchar(20) DEFAULT NULL, + `salary` decimal(8,2) DEFAULT NULL, + PRIMARY KEY (`ID`), + KEY `dept_name` (`dept_name`), + CONSTRAINT `instructor_ibfk_1` FOREIGN KEY (`dept_name`) REFERENCES `department` (`dept_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 | +``` + +可以看到是instructor_ibfk_1。 + +#### (2)带别名的外键 + +```mysql +CREATE TABLE instructor ( + ID char(5), + name varchar(20) NOT NULL, + dept_name varchar(20), + salary numeric(8, 2), + PRIMARY KEY (ID), + CONSTRAINT fk_dept_name FOREIGN KEY (dept_name) REFERENCES department (dept_name) +); +``` + +查看生成的外键名: + +```mysql +show create table instructor + +| instructor | CREATE TABLE `instructor` ( + `ID` char(5) NOT NULL, + `name` varchar(20) NOT NULL, + `dept_name` varchar(20) DEFAULT NULL, + `salary` decimal(8,2) DEFAULT NULL, + PRIMARY KEY (`ID`), + KEY `fk_dept_name` (`dept_name`), + CONSTRAINT `fk_dept_name` FOREIGN KEY (`dept_name`) REFERENCES `department` (`dept_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 | +``` + +可以看到是fk_dept_name。 + +#### (2)删除外键 + +```mysql +alter table instructor drop foreign key fk_dept_name; +``` + +#### (3)添加外键 + +```mysql +alter table instructor add constraint fk_dept_name foreign key(dept_name) references department(dept_name); +``` + +### 2.2.2 on update和on delete + +这是数据库外键定义的一个可选项,用来设置当主键表中的被参考列的数据发生变化时,外键表中响应字段的变换规则的。update 则是主键表中被参考字段的值更新,delete是指在主键表中删除一条记录: +on update 和 on delete 后面可以跟的词语有四个:no action , set null , set default ,cascade。 +no action 表示 不做任何操作, +set null 表示在外键表中将相应字段设置为null +set default 表示设置为默认值(restrict) + +(1)on delete cascade学习 + +cascade 表示级联操作,就是说,如果主键表中被参考字段更新,外键表(子表)中也更新,主键表(父表)中的记录被删除,外键表(子表)中改行也相应删除。 + +还是上述例子,首先创建`instructor`与`department`表。 + +- **创表** + +**父表**: + +```mysql +CREATE TABLE department ( + dept_name varchar(20) PRIMARY KEY NOT NULL, + building varchar(20), + budget int +); +``` + +**子表**: + +```mysql +CREATE TABLE instructor ( + ID char(5), + name varchar(20) NOT NULL, + dept_name varchar(20), + salary numeric(8, 2), + PRIMARY KEY (ID), + CONSTRAINT fk_dept_name FOREIGN KEY (dept_name) REFERENCES department (dept_name) + ON UPDATE cascade +); +``` + +- **插入数据** + +> 当父表无数据,直接向子表插入数据,报错 + +```mysql +mysql> insert into instructor values(1,'小米','小米手机部门',1000.2); +ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`ddl`.`instructor`, CONSTRAINT `fk_dept_name` FOREIGN KEY (`dept_name`) REFERENCES `department` (`dept_name`) ON DELETE CASCADE) +``` + +不管有没有on delete cascade都会出现这个问题。 + +现在往先往父表中插入数据: + +```mysql +mysql> insert into department values('小米手机部门','北京',10000); +Query OK, 1 row affected (0.07 sec) +``` + +往从表插入数据: + +```mysql +mysql> insert into instructor values(1,'小米','小米手机部门',1000.2); +Query OK, 1 row affected (0.08 sec) +``` + +查看两表数据: + +```mysql +mysql> select * from department; ++--------------------+----------+--------+ +| dept_name | building | budget | ++--------------------+----------+--------+ +| 小米手机部门 | 北京 | 10000 | ++--------------------+----------+--------+ +1 row in set (0.00 sec) + +mysql> select * from instructor; ++----+--------+--------------------+---------+ +| ID | name | dept_name | salary | ++----+--------+--------------------+---------+ +| 1 | 小米 | 小米手机部门 | 1000.20 | ++----+--------+--------------------+---------+ +1 row in set (0.00 sec) +``` + +- 删除 + +> 父子表都有数据,删除子表数据 + +```mysql +mysql> delete from instructor where dept_name='小米手机部门'; +Query OK, 1 row affected (0.13 sec) + +mysql> select * from instructor; +Empty set (0.00 sec) + +mysql> select * from department; ++--------------------+----------+--------+ +| dept_name | building | budget | ++--------------------+----------+--------+ +| 小米手机部门 | 北京 | 10000 | ++--------------------+----------+--------+ +1 row in set (0.01 sec) +``` + +发现子表数据被删除,父表数据不变。 + +> 父子表都有数据,删除父表数据 + +```mysql +mysql> select * from instructor; ++----+--------+--------------------+---------+ +| ID | name | dept_name | salary | ++----+--------+--------------------+---------+ +| 1 | 小米 | 小米手机部门 | 1000.20 | ++----+--------+--------------------+---------+ +1 row in set (0.00 sec) + +mysql> select * from department; ++--------------------+----------+--------+ +| dept_name | building | budget | ++--------------------+----------+--------+ +| 小米手机部门 | 北京 | 10000 | ++--------------------+----------+--------+ +1 row in set (0.00 sec) + +mysql> delete from department where dept_name='小米手机部门'; +Query OK, 1 row affected (0.10 sec) + +mysql> select * from department; +Empty set (0.00 sec) + +mysql> select * from instructor; +Empty set (0.00 sec) +``` + +父表数据被删除,子表联带被删除! + +

如果没有使用`on delete/update cascade`,不能删除或更新父表数据,当删除父表的数据时候报错!如下所示:

+ +```mysql +ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`ddl`.`instructor`, CONSTRAINT `instructor_ibfk_1` FOREIGN KEY (`dept_name`) REFERENCES `department` (`dept_name`)) +``` + +要想删除,必须先子表再父表!如下所示: + +```mysql +mysql> delete from instructor where dept_name='小米手机部门'; +Query OK, 1 row affected (0.15 sec) + +mysql> select * from instructor; +Empty set (0.00 sec) + +mysql> select * from department; ++--------------------+----------+--------+ +| dept_name | building | budget | ++--------------------+----------+--------+ +| 小米手机部门 | 北京 | 10000 | ++--------------------+----------+--------+ +1 row in set (0.00 sec) + +mysql> delete from department where dept_name='小米手机部门'; +Query OK, 1 row affected (0.11 sec) + +mysql> select * from department; +Empty set (0.00 sec) +``` + +> 父子表都有数据,更新父表主键或者子表外键,都无效 + +如果想要更新,那么就必须换成`on update cascade`。 + +上述`on delete cascade`换成`on update cascade`,可以发现只能更新父表的主键,同时父子表数据都会被更新,但是在子表的外键上做更新操作无效! + +```mysql +mysql> update departme set dept_name='苹果手机部门' where dept_name='小米手机部门' +Query OK, 1 row affected (0.12 sec) +Rows matched: 1 Changed: 1 Warnings: 0 + +mysql> select * from department; ++--------------------+----------+--------+ +| dept_name | building | budget | ++--------------------+----------+--------+ +| 苹果手机部门 | 北京 | 10000 | ++--------------------+----------+--------+ +1 row in set (0.00 sec) + +mysql> select * from instructor; ++----+--------+--------------------+---------+ +| ID | name | dept_name | salary | ++----+--------+--------------------+---------+ +| 1 | 小米 | 苹果手机部门 | 1000.20 | ++----+--------+--------------------+---------+ +1 row in set (0.00 sec) + +mysql> update instructor set dept_name='小米手机部门' where dept_name='苹果手机 部门'; +Query OK, 0 rows affected (0.00 sec) +Rows matched: 0 Changed: 0 Warnings: 0 + +mysql> select * from instructor; ++----+--------+--------------------+---------+ +| ID | name | dept_name | salary | ++----+--------+--------------------+---------+ +| 1 | 小米 | 苹果手机部门 | 1000.20 | ++----+--------+--------------------+---------+ +1 row in set (0.00 sec) + +mysql> select * from department; ++--------------------+----------+--------+ +| dept_name | building | budget | ++--------------------+----------+--------+ +| 苹果手机部门 | 北京 | 10000 | ++--------------------+----------+--------+ +1 row in set (0.00 sec) +``` + +### 2.2.3 总结 + +on delete cascade 不能更新父表主键或子表外键,删除父表主键数据会将子表联同删除,删除子表外键数据不影响父表。而on update只能删除子表外键数据,不能删除父表主键数据,只能更新父表的主键,同时父子表数据都会被更新,但是在子表的外键上做更新操作无效。如果没有使用`on delete/update cascade`,不能删除或更新父表数据。 \ No newline at end of file