「数据库」 SQL

Posted by Dawn-K's Blog on June 4, 2020

创建表格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Create table Courses
( Cno  char(4), 
Cname char(40), 
Cpno char(4), 
Ccredit int, 
primary key (Cno) ,  -- 主键
foreign Key (Cpno) REFERENCES Courses(Cno) -- 外键
);

CREATE TABLE Student
(
Sno char(6), 
Sname char(10), 
Ssex  char(1), 
Sage  int, 
Sdept char(2), 
primary key (Sno)
); 

Create table SC
( Sno  char(11), 
Cno char(4), 
Grade  int, 
primary key (Sno , Cno ), 
foreign key(Sno ) references Students (Sno), 
foreign key (Cno ) references Courses (Cno )); 

-- 修改表:在学生表中添加字段Sclass,字符类型,长度20
    Alter table Students  add Sclass char(20)
-- 将年龄的数字类型由字符型改为整型
    Alter table Students alter column Sage int
-- 增加课程名称必须取唯一值的约束条件
    Alter table Courses add unique (Cname)

-- 删除表Students:
    -- Restrict: 表删除有限制条件,不能被其他表的约束所引用
    --  Cascade:把一个基本表的定义连同表上的所有记录、索引以及由此基本表导出的所有视图都删除,并释放相应的存储空间
    Drop table Students cascade

单表查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
-- 用户可以通过指定别名来改变查询结果的列标题
-- NAME          BIRTH                 BIRTHDAY   DEPARTMENT
-- 李勇        Year of birth:           1999          cs
    select Sname NAME, Year of birth BIRTH,
            2020-Sage BIRTHDAY, 
            islower(Sdept) DEPARTMENT
    from   Students;

-- 查询年龄在20岁以下的学生姓名及其年龄
    select   Sname, Sage
    from Students
    where Sage < 20;

-- 查询考试成绩有不及格的学生的学号(注意去重)
	select distinct sno 
	from SC
	where Grade<60

-- 查询年龄在20~23岁之间的学生姓名、所在系,年龄
-- 与between…and…相对的谓词是not between…and…
    select Sname, Sdept, Sage
    from Students
    where Sage between 20 and 23;

-- 查询信息系、数学系和计算机系学生的姓名和性别
-- 与in相对的谓词是Not in
    select Sname, Ssex
    from Students
    where Sdept in (IS, MA, CS);

-- 查询所有姓刘的学生的姓名、学号和性别(not like 可以表示相反的意思)
    select Sname, Sno, Ssex
    from Students
    where Sname like ‘刘%’;

-- 查询姓欧阳且全名为三个汉字的学生姓名
    select Sname
    from Students
    where Sname like ‘欧阳__;

-- 查询DB_Design课程号和学分(用escape表示转移了下划线)
    select Cno, Ccredit
    from Courses
    where Cname like DB\_Design escape ‘\’;

-- 查询缺考学生的学号和课号(is 反义词是 is not ,null 不可以用等号)
    select Sno, Cno
    from SC
    where Grade is null;

-- 查询计算机系年龄在20岁以下的学生的姓名(and 可替换成 or ,and 优先级大于 or 可通过加括号来划定优先级 )
    select Sname
    from Students
    where Sdept=CS  and Sage<20;

-- 查询选修了3号课程的学生学号和成绩,要求查询结果按成绩降序排列(升序是 asc)
    select Sno, Grade
    from SC
    where Cno=3 
    order by Grade desc;

多表查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
-- 参加集合操作的各查询结果的列数必须相同,对应项的数据类型也必须相同
-- 在union, intersect, except中
-- 缺省情况下:消除重复元组
-- 在union, intersect, 或except后加上all,保留重复元组
    select *
    from Students
    where Sdept=CS
    Union -- 表示'或'
    -- intersect 表示'且'
    -- except  表示 '差'
    select *
    from Students
    where Sage <= 19;

-- 在 where 中使用比较运算符来作为连接两个表的条件
-- 但是可能会产生 两列内容一样的情况,可以手动修改 select 以去掉重复的列
    select Students.*, SC.*
    from Students, SC
    where Students.Sno = SC.Sno;

-- 查询租借过船只的水手姓名和租借的日期
    select Sailors.name, Reserve.date
    from Sailors, Reserve
    where Sailors.Sid = Reserve.Sid;

-- 查询每门课的课号及其间接先修课的课号
    select C1.Cno, C2.Cpno
    from Courses C1, Courses C2
    where C1.Cpno=C2.Cno; 

-- 外连接:在一般连接中,只有满足条件的元组才可以作为结果输出, 外连接则不同(左、右外连接),把舍弃的元组保留在结果关系中,而在其他属性上填空值。
-- 内连接(是学生,也是雇员)
Students INNER JOIN Employee 
        on  ( Students.name = Employee.name )

-- 左外连接(是学生,可以不是雇员)
Students LEFT OUTER JOIN Employee

-- 全外连接(可以不是学生,可以不是雇员)
Students FULL OUTER JOIN Employee

不相关子查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
-- 一个查询块可以放在另一个查询块的where子句或having短语的条件中
-- 甚至是SELECT子句和FROM子句中

-- 不相关子查询求解方法由里向外
-- 相关子查询求解方法由外向里

-- 使用 in 来链接(适合子查询结果是多值)(感觉这样提高了可读性)
-- 不知道这个 in 有没有反义词
    select *
    from Students
    where Sdept in (select Sdept
                    from Students
                    where Sname=‘刘晨’)
-- 和上文等价
    select S2.*
    from Students S1, Students S2,
    where S1.Sname = ‘刘晨’ and
          S1.Sdept = S2.Sdept;

-- 使用比较运算符,要求子查询结果是单值
    select *
    from Students
    where Sage =   (select Sage 
                    from Students
                    where Sno=20140001);

-- 带有 any 或 all 的子查询必须和比较运算符同用
-- 查询比信息系某一学生年龄小的其他系学生姓名和年龄
    select Sname, Sage
    from Students
    where Sage <any (select Sage
                    from Students
                    where Sdept=IS)
                    and Sdept != IS ;                 
-- 等价,使用集函数效率高
    select Sname, Sage
    from Students
    where Sage < (select max(Sage)
                  from Students
                  where Sdept=IS)
          and Sdept!=IS ;                               

相关子查询

1
2
3
4
5
6
7
8
9
10
11
12
-- 相关子查询
-- exists 加上子查询 返回值是 true 或 false

-- 查询所有选修了1号课程的学生的姓名(没有选修的 写作 not exists )
-- 计算顺序是从外向内,Students会被内层的子查询使用
    select Sname
    from Students
    where exists  ( select *
                    from SC
                    where Sno=Students.Sno 
                          and     Cno=1);

$\forall x(P) \equiv \neg(\exist x(\neg P) ) $ 通过这个公式可以起到全称量词的效果

核心思想是找反例, 就是如果对于某个学生, 找不到一个课他没上过, 那么他就上过所有的课

聚集函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
count sum avg max min

-- 查找最年长的水手的年龄和名字
Select S.sname, S.age
From Sailors S
Where S.age=(select max(S2.age) from Sailors S2)

-- 查询选修了课程的学生人数
    select count (distinct Sno)
    from SC;

-- 平均值
-- 求选修1号课程的学生的平均成绩
    select avg(Grade)
    from SC
    where Cno=1;

-- 分组
-- 将查询结果按一列或多列值分组, 目的是将集函数作用到组上
-- 求选课人数超过10人的课程号及其人数
    select Cno, count(Sno)
    from SC
    group by Cno
    having count(Sno)>10; -- 组内条件

-- 在18岁以上水手中,对于每个rating级别中最少有两个水手以上的组中最年轻水手的年龄
    SELECT S.rating, Min(S.age)
    FROM Sailors S
    WHERE S.age>18
    GROUP BY S.rating
    HAVING COUNT(*)>1

-- 修改
update   Students  set  Sage = 22 
update   Students  set  Sage = Sage + 1;