跳转至

数据库原理与应用

第一章 绪论

1.1 数据库系统概述

1.1.1 4个基本概念

  • 数据 Data
  • 数据库 Database
  • 数据库管理系统 DBMS
  • 数据库系统 DBS

1.1.2 数据管理技术的产生与发展

  • 人工管理阶段
  • 文件系统阶段
  • 数据库系统阶段

1.2 数据模型

1.2.1 两类数据模型

  • 概念模型
  • 逻辑模型和物理模型

1.3 数据库系统的结构

从数据库最终用户角度看,数据库系统结构分为

  • 单用户结构
  • 主从式结构
  • 分布式结构
  • 客户-服务器 C/S
  • 浏览器-应用服务器 B/S

1.3.2 数据库系统的三级模式结构

  • 模式 Schema
  • 外模式 External Schema
  • 内模式 Internal Schma

1.4 数据库系统的组成

第二章 关系数据库

2.1 关系数据结构及形式化定义

2.1.1 关系

  • 单一的数据结构 —— 关系
  • 逻辑结构 —— 二维表

    从用户角度,关系模型中数据的逻辑结构是一张二维表

  • 建立在几何代数的基础上

1. 域 Domain

一组具有相同数据类型的值的集合

2. 笛卡尔积

给定一组域D1, D2, ..., Dn,允许其中某些域时相同的。

D1, D2, ..., Dn的笛卡尔积为:

\[D_1 \times D_2 \times ... \times D_n = \{(d_1, d_2, ..., d_n) | d_i \in D_i, i = 1, 2, ..., n\}\]
  • 元组

    笛卡尔积中的每一个元素(d1, d2, ..., dn)叫做一个 n 元组

3. 关系 Relation

(1) 关系

\(D_1\times D_2 \times ... \times D_n\)的子集叫做在域 D1, D2, ..., Dn 上的关系,表示为

\[R(D_1, D_2, ..., D_n)\]

(4) 关系的表示

关系也是一个二维表,表的每行对应一个元组,每列对应一个域

(5) 属性

  • 关系中不同列可以对应相同的域

  • 为了加一区分,必须对每列起一个名字,称为属性(Attribute)

  • n 目关系必有 n 个属性

(6) 码

  • 候选码 Candidate key

    若关系中的某一属性组的值能唯一地标识一个元组,则称该属性组为候选码

    简单的情况:候选码质保函一个属性

  • 全码 All-key

    最极端的情况:关系模式的所有属性组是这个关系模式的候选码,称为全码(All-key)

(7) 三类关系

  • 基本关系

    实际存在的表

  • 查询表

    查询结果对应的表

  • 视图表

    由基本表或其他视图表导出的表,是虚表,不对应实际存储的数据

2.1.2 关系模式

1. 什么是关系模式
  • 关系模式(Relation Schema)是型

  • 关系是值

  • 关系模式是对关系的描述

    • 元组集合的结构

      • 属性构成
      • 属性来自的域
      • 属性与域之间的映象关系
    • 完整性约束条件

2. 定义关系模式

关系模式可以形式化的表示为:

\[R(U, D, DOM, F)\]
  • R 关系名
  • U 组成该关系的属性名集合
  • D U 中属性所来自的域
  • DOM 属性象域的映像集合
  • F 属性间数据的依赖关系的集合

2.1.3 关系数据库

2.2 关系操作

2.2.1 基本的关系操作

  • 常用的关系操作

    • 查询操作:选择、投影、连接、除、并、差、交、笛卡尔积

    • 数据更新:插入、删除、修改

  • 关系操作的特点

都是集合操作方式:操作的对象和结果都是集合

2.2.2 关系数据库语言的分类

  • 关系代数语言

    • 用对关系的运算来表达查询要求
    • 代表:ISBL
  • 关系演算语言:用谓词来表达查询要求

    • 元组关系演算语言
    • 域关系演算语言
  • 具有关系代数和关系演算双重特点的语言

    • 代表:SQL(Structured Query Language)

2.3 关系的完整性

就是表的约束规则,有三类完整性约束:

E A R

2.3.1 实体完整性

  • 若属性 A 是基本关系 R 的主属性,则属性 A 不能取空值

2.3.2 参照完整性

1. 关系间的引用
  • 在关系模型中实体及实体键的联系都是用关系来描述的,自然存在着关系与关系间的引用

例2.1 学生实体、专业实体

学生(学号,姓名,性别,专业号,年龄)

专业(专业号,专业名)

  • 学生关系引用了专业关系的主码“专业号”
  • 学生关系中的“专业号”值必须是确实存在的专业的专业号

外码连的是另一个表的主码

例2.3 学生实体及其内部的一对多联系

学生(学号,姓名,性别,专业号,年龄,班长)

  • 学号是主码,班长是外码,引用了本关系的学号

  • 班长必须是确实存在的学生的学号

2. 外码(Foreign Key)
  • 设 F 是基本关系 R 的一个或一组属性,但不是关系 R 的码

    如果 F 与基本关系 S 的主码 Ks 相对应,则称 F 是 R 的外码

  • 基本关系 R 称为参照关系(Referencing Relation)

  • 基本关系 S 称为被参照关系(Referenced Relation) 或目标关系(Target Relation)

  • 关系 R 和 S 可以是同一个关系

  • 目标关系 S 的主码 Ks 和参照关系的外码 F 必须定义在同一个(或一组)域上

  • 外码不一定要与相应的主码同名(有时候相同含义但名字不完全相同)

3. 参照完整性规则

数据库中,引用必须有效✍️✍️✍️

外码是引用别人的主码的

所以外码要么就是空值,要么一定能在别人的主码里找到

2.3.3 用户定义的完整性

就是自己定义的规则,例如“课程号”属性必须取唯一值,“课程名”属性不能为空。。。

2.4 关系代数

关系代数运算符:

集合运算符

运算符 含义
\(\cup\)
\(-\)
\(\cap\)
\(\times\) 笛卡尔积

专门的关系运算符

运算符 含义
\(\sigma\) 选择
\(\pi\) 投影
\(\bowtie\) 连接
\(\div\)

2.4.1 传统的集合运算

(1) 并 Union
  • R 和 S

    • 具有相同的目 n(既两个关心都有 n 个属性)
    • 相应的属性取自同一个域
  • \(R \cup S\)

    • 仍为 n 目关系,由属于 R 或属于 S 的元组组成
\[R \cup S = \{t \mid t \in R \lor t \in S \}\]
(2) 差 Difference
  • R 和 S

    • 具有相同的目 n(既两个关心都有 n 个属性)
    • 相应的属性取自同一个域
  • \(R - S\)

    • 仍为 n 目关系,由属于 R 而不属于 S 的元组组成
\[R - S = \{t \mid t \in R \land t \notin S \}\]
(3) 交 Intersection
  • R 和 S

    • 具有相同的目 n(既两个关心都有 n 个属性)
    • 相应的属性取自同一个域
  • \(R \cap S\)

    • 仍为 n 目关系,由既属于 R 又属于 S 的元组组成
\[R \cap S = \{t \mid t \in R \land t \in S \}\]
(4) 笛卡尔积 Cartesian Product
  • 是广义的笛卡尔积(Extended Cartesian Product)
  • R: n 目关系,k1 个元组
  • S: m 目关系,k2 个元组
  • \(R \times S\)

    • 列:(n + m) 列元组的集合

      • 元组的前 n 列是关系 R 的一个元组
      • 后 m 列是关系 S 的一个元组
    • 行:\(k_1 \times k_2\)个元组

\[R \times S = \{ \overset{\frown}{t_r t_s} \mid t_r \in R \land t_s \in S \} \]

2.4.2 专门的关系运算

先引入几个记号

(1) \(R, t \in R, t[A_i]\)

设关系模式为 \(R(A_1, A_2, ..., A_n)\)

他的一个关系设为 R

\(t \in R\) 表示 \(t\)\(R\) 的一个元组 (tuple)

\(t[A]\) 则表示 元组 \(t\) 中相应于属性 \(A_i\) 的一个分量

(2) \(A, t[A], \bar{A}\)

\(A = \{A_{i1}, A_{i2}, ..., A_{ik}\}\),其中 \(A_{i1}, A_{i2}, ..., A_{ik}\)\(A_1, A_2, ..., A_n\)中的一部分,则\(A\)称为属性列或属性组。

\(t[A] = (t[A_{i1}], t[A_{i2}], ..., t[A_{ik}])\) 表示元组 \(t\)在属性列 \(A\)上诸分量的集合。

(3) \(\overset{\frown}{t_r t_s}\)

\(R\)\(n\) 目关系,\(S\)\(m\) 目关系。 \(t_r \in R, t_s \in S, \overset{\frown}{t_r t_s}\) 称为元组的连接

\(\overset{\frown}{t_r t_s}\) 是一个 \(n + m\) 列的元组,前 \(n\) 个分量为 \(R\) 中的一个 \(n\) 元组,后 \(m\) 个分量为 \(S\) 中的一个 \(m\) 元组

(4) 象集 \(Z_x\)

给定一个关系 \(R(X, Z)\)\(X\)\(Z\) 为属性组。

\(t[X] = x\)时,\(x\)\(R\) 中的象集(Images Set) 为:

\[Z_X = \{ t[Z] \mid t \in R , t[X] = x \}\]

他表示 \(R\) 中属性组 \(X\) 上值为 \(x\) 的诸元组在 \(Z\) 上分量的集合

R

x1 Z1
x1 Z2
x1 Z3
x2 Z2
x2 Z3
x3 Z1
x3 Z3
  • x1 在 R 中的象集
\[Z_{x_1} = \{ Z_1, Z_2, Z_3 \}\]
  • x2 在 R 中的象集
\[Z_{x_2} = \{ Z_2, Z_3 \}\]
  • x3 在 R 中的象集
\[Z_{x_3} = \{ Z_1, Z_3 \}\]

Info

象集,是根据一个元组,由原先的关系中计算得到的一组元组的集合

其中依据的元组是从原先的关系中抽取出来的,附带有原先关系中的属性

尽管是元组的集合,我们不把象集叫做关系,因为我们不关注象集的属性,只关注象集内的值

1. 选择 Selection

SELECT

从关系 R 中选取满足条件的元组

  • 选择又称为限制 Restriction
  • 选择运算符的含义

    • 在关系 R 中选择满足给定条件的诸元组
    \[\sigma_F(R) = {t \mid t \in R \land F(t) = True}\]
    • \(F\): 选择条件,是一个逻辑表达式,取值为 True or False

      • 基本形式为:\(X_1 \theta Y_1\)
      • \(\theta\) 表示比较运算符,可以是\(<,>,<=,>=,=,<>\)

例2.4 查询信息系(IS系)全体学生

\[\sigma_{Sdept = 'IS'}(Student)\]
2. 投影 Projection
  • 从 R 中选择出若干熟悉列组成新的关系
\[\Pi_A(R) = {t[A] \mid t \in R}\]
\[A: R 中的属性列\]

Projection

投影是从关系中挑选一些列

投影之后不仅取消了原关系中的某些列,而且还可能取消某些元组(避免重复行)

3. 连接 Join

连接也称 \(\theta\) 连接

从两个关系的笛卡尔积中选取属性减满足一定条件的元组

\[R \bowtie S_{A \theta B} = \{ \overset{\frown}{t_r t_s} \mid t_r \in R \land t_s \in S \land t_r[A] \theta t_S[B] \}\]
  • \(A 和 B: 分别为R 和 S 上度数相等且可比的属性组\)
  • \(\theta\): 比较运算符

连接 Join

连接运算从 R 和 S 的广义笛卡尔积 \(R \times S\)中选取\(R\)关系在\(A\)属性列上的值与\(B\)属性组上的值满足比较关系\(\theta\)的元组

连接操作是从行的角度进行运算

自然连接还需要取消重复列,所以是同时从行和列的角度进行运算

  • 两类常用的连接运算

等值连接

  • \(\theta\)\(=\)的连接运算称为等值连接

  • 从关系\(R与S\)的笛卡尔积中选取\(A、B\)属性值相等的那些元组,即:

\[R \bowtie S_{A=B} = \{\overset{\frown}{t_r t_s} \mid t_r \in R \land t_s \in S \land t_r[A] = t_s[B]\}\]

自然连接 Natural join

  • 自然连接是一种个特殊的等值连接

    • 两个关系中进行比较多分量必须是相同的属性组
    • 在结果中把重复的属性列去掉
  • 自然连接的含义

    R和S具有相同的属性组 B

    \[R \bowtie S = \{\overset{\frown}{t_r t_s} [U-B] \mid t_r \in R \land t_s \in S \land t_r[B] = t_s[B]\}\]

写法上就是连接 \(\bowtie\) 运算符不用加下标了

  • 悬浮元组 Dangling tuple

    • 两个关系在做自然连接时,关系 R 中某些元组有可能在 S 中不存在公共属性上值相等的元组,从而造成 R 中这些元组在操作时被舍弃了,这些被舍弃的元组称为悬浮元组
  • 外连接 Outer Join

    • 如果把悬浮元组也保存在结果关系中,而在其他属性上填空值Null,就叫做外连接

    • 左外连接 Left Outer Join

      • 只保留左边关系 R 中的悬浮元组
    • 右外连接

      • 只保留右边关系 S 中的悬浮元组

例2.8 关系 R 和关系 S 如下所示:

  • R
A B C
a1 b1 5
a1 b2 6
a2 b3 8
a2 b4 12
  • S
B E
b1 3
b2 7
b5 10
b3 2
b2 2
  • 外连接
A B C E
a1 b1 5 3
a1 b2 6 7
a1 b2 8 2
a2 b3 8 2
a2 b4 12 NULL
NULL b5 NULL 10
  • 左外连接

保留 R 中所有行

A B C E
a1 b1 5 3
a1 b2 6 7
a1 b2 6 2
a2 b3 8 2
a2 b4 12 NULL
4. 除运算 Division

给定关系 \(R(X, Y)\)\(S(Y, Z)\),其中 \(X, Y, Z\) 为属性组

\(R\) 中的 \(Y\)\(S\) 中的 \(Y\) 可以有不同的属性名,但必须出自相同的域集

\(R\)\(S\) 的除运算得到一个新的关系 \(P(X)\)

\(P\)\(R\) 中满足下列条件的元组在 \(X\) 属性列上的投影:

元组在 \(X\) 上分量值 \(x\) 的象集 \(Y_x\)包含\(S\)\(Y\) 上投影的集合

\[R \div S = \{ t_r[X] \mid t_r \in R \land \Pi_Y(S) \subseteq Y_X \}\]

除操作是同时从行和列角度进行运算

alt text

对于 \(S\) 的计算很简单,只要从 \(S\) 中提取 \(Y\) 属性的所有元组,即 \(\Pi_Y (S)\)

然后找 \(R[X]\) ( \(t_r \in R, t_r[X]\) ) 中哪些元组的象集包含住了 \(\Pi_Y (S)\)

求的就是满足这个条件的 \(t_r\) 的集合组成的新关系

例2.9

R

A B C
a1 b1 c2
a2 b3 c7
a3 b4 c6
a1 b2 c3
a4 b6 c6
a2 b2 c3
a1 b2 c1

S

B C D
b1 c2 d1
b2 c1 d1
b2 c3 d2

\(R \div S:\)

A
a1

R 和 S 共有的列:B, C

\(\Pi_{B, C} = \{ \{b1, c2 \}, \{b2, c1 \}, \{ b2, c3 \} \}\)

\(R[A]\) 中的元组中的象集包含了 \(\Pi_{B, C}\) 的只有 a1

例2.10 查询至少选修1号课程和3号课程的学生号码

寻找:选课序号中包含 1, 3,即求:

建立临时关系 K: Cno{1, 3}

\[\Pi_{Sno, Cno}(SC) \div K\]

然后有4道习题

跳转链接【数据库第2章 关系数据库 (下)】 【精准空降到 1:08:13】

2.5 *关系演算

第三章 关系数据库标准语言 SQL

3、4、5 就是 SQL 基础

3.1 SQL 概述

Structured Query Language

布拉布拉有一堆概念

3.1.2 SQL 的特点

  1. 综合统一

    • 集 DDL, DML, DCL 于一体
    • 。。。
  2. 高度非过程化

    只要关注要做什么,不用关心怎么做的

  3. 面向集合的操作方式

  4. 以同一种语法结构提供多种使用方式

    既是独立的语言,有是嵌入式语言(能够嵌入高级语言?)

  5. 语言简洁,易学易用

    完成核心功能只用了9个动词:

    alt text

3.2 学生-课程数据库

我们的课本的数据库实例要比这个复杂地多,那个b袁神说要背下来,还没怎么看

这里的这个数据库是这样的:

  • 学生-课程模式 S-T:

    学生表:Student(Sno, Sname, Ssex, Sage, Sdept)

    课程表:Course(Cno, Cname, Cpno, Ccredit)

    学生选课表:SC(Sno, Cno, Grade)

3.3 数据定义

定义分为:

  • 模式定义
  • 表定义
  • 视图和索引的定义

alt text

sql 中的关键字不区分大小写

3.3.1 模式的定义与删除

1. 定义模式

例3.1 为用户 WANG 定义一个学生-课程模式 S-T

create schema "S-T" authorization WANG;

例3.2 create schema authorization WANG;

该语句没有指定<模式名>,<模式名>隐含为<用户名>

  • 定义模式实际上定义了一个命名空间
  • 在空间中可以定义该模式包含的数据库对象

    例如基本表、视图、索引

  • create schema中可以接受create table, create viewgrant子句

    create schema <模式名> authorization <用户名>[<表定义子句>|<视图定义子句>|<授权定义子句>]
    

例3.3 为用户 ZHANG 创建一个模式 TEST,并在其中定义一个表 TAB1

create schema TEST authorization ZHANG
create table TAB1(COL1 SMALLINT, 
                  COL2 INT, 
                  COL3 CHAR(20)
);

sql 中的数据类型也不区分大小写

2. 删除模式
drop schema <模式名> <cascade|restrict>
  • cascade 级联

    删除模式的同时把该模式中所有的数据库对象全部删除

  • restrict 限制

    如果该模式中定义了下属的数据库对象(表、视图等),则拒绝删除

    没有时可以执行删除

3.3.2 基本表的定义、删除与修改

1. 定义基本表
create table <表名> (
    <列名><数据类型>[<列级完整性约束条件>]
    [,<列名><数据类型>[<列级完整性约束条件>]]
    ...
    [,<表级完整性约束条件>]
);
  • <表名>:所要定义的基本表的名字
  • <列名>:组成给表的各个属性(列)
  • <列级完整性约束条件>:涉及相应属性列的完整性约束条件
  • <表级完整性约束条件>:涉及一个或多个属性列的完整性约束条件

推荐写法

create table ... (
    /*框架*/

    /*主码*/
    primary key(属性名1, ...), 
    /*外码*/
    foreign key(属性名1) references (参考了表的哪个列),
    foreign key(属性名2) references (参考了表的哪个列)
);

例3.5 建立学生表Student,学号是主码,姓名取值唯一

create table Student (
    Sno char(9) primary key, 
                    /*列级完整性约束条件:Sno是主码*/
    Sname char(20) unique, /*Sname取唯一值*/
    Ssex char(2), 
    Sage smallint, 
    Sdept char(20)
);

例3.7 建立一个学生选课表SC

create table SC (
    Sno char(9), 
    Cno char(4),
    Grade smallint, 
    primary key(Sno, Cno), 
        /*表示主码由两个属性构成,必须作为表级完整性进行定义*/
    foreign key(Sno) references Student(Sno), 
        /*表级完整性约束条件,Sno是外码,被参照表是Student*/
    foreign key(Cno) references Course(Cno)
        /*表级完整性约束条件,Cno是外码,被参照表是Course*/
);
2. 数据类型

alt text

常见数据类型表格

3. 模式与表
  • 每个基本表都属于一个模式
  • 一个模式包含多个基本表
  • 定义基本表所属模式(关注前两个方法就行)

    • 方法一:在表名中明显地给出模式名
    create table "S-T".Student(...);
    create table "S-T".Course(...);
    create table "S-T".SC(...);
    
    • 方法二:在创建模式语句中同时创建表

      在模式定义里有讲

    • 方法三:设置所属的模式

4. 修改基本表
alter table <表名>
[add [column] <新列名><数据类型>[完整性约束]]
[add <表级完整性约束>]
[drop [column] <列名> [cascade|restrict]]
[drop constraint <完整性约束名> [restrict|cascade]]
[alter column <列名><数据类型>];

例3.8 想Student表增加“入学时间”列,其数据类型为日期型

alter table Student add S_entrance date;

新增加的列一律为空值

例3.9 将年龄的数据类型由字符型改为整数

alter table Student alter column Sage INT;

例3.10 增加课程名称必须取唯一值的约束条件

alter table Course add unique(Cname);
5. 删除基本表
drop table <表名> [restrict|cascade];
  • restrict

    删除有限制,例如当基本表上建有视图时

  • cascade

    删除时全部删光!没有限制

3.3.3 索引的建立与删除

  • 建立索引的目的:加快查询速度
  • 关系数据库管理系统中常见索引:

    • 顺序文件上的索引
    • B+ 树索引
    • 散列(hash)索引
    • 位图索引
  • 特点:

    • B+树索引具有动态平衡的优点
    • hash索引具有查找速度快的特点
  • 谁可以建立索引?

    数据库管理员表的属主可以建立索引

  • 谁维护索引?

    关系数据库管理系统自动完成

  • 怎么选择使用哪种索引?

    关系数据库管理系统自动选择合适的所有作为存取路径,用户不必也不能显示地选择索引

1. 建立索引

create [unique] [cluster] index <索引名>
on <表名>(<列名>[<次序>][,<列名>[<次序>]]...);
- <次序>:指定索引值的排列次序

- 升序:ASC
- 降序:DESC
- 缺省:默认ASC
  • unique:此索引的每一个索引值只对应唯一的数据记录
  • cluster:表示要建立的索引是聚簇索引(类似按首字母分好类就是聚簇索引)

Tip

unique:建表时限制数据不能重复

distinct:查询数据时去重

例3.13 为学生-课程数据库中的Student, Course, Sc三个表建立索引。Student表按学号升序建立唯一索引,Course表按课程号升序建唯一所有,SC表按学号升序和课程号降序建唯一索引

create unique index Stusno on Student(Sno);
create unique index Coucno on Course(Cno);
create unique index SCno on SC(Sno ASC, Cno DESC);
2. 修改索引
alter index <旧索引名> rename to <新索引名>

例3.14 将SC表的SCno索引名改为SCSno

alter index SCno rename to SCSno;
3. 删除索引
drop index <索引名>;

3.3.4 数据字典

不重要

3.4 数据查询

select [all|distinct] <目标列表达式>[,<目标列表达式>]...
from <表名或视图名>[,<表名或视图名>]...|(select语句)
    [as]<别名>
[where <条件表达式>]
[group by <列名1> [having <条件表达式>]]
[order by <列名2> [asc|desc]];
  • select 子句

    指定要显示的属性列

  • from 子句

    指定查询对象

  • where 子句

    指定查询条件

  • group by 子句:

    对查询结果按指定列的值分组,该属性列值相等的元组为一个组。通常会在每组中作用聚集函数

  • having 短语

    只有满足指定条件的组才予以输出

  • order by 子句

    对查询结果表按指定列值的升序或降序排序

Tip

1 单表查询章节就是把这几个子句讲清楚

2-4 连接查询、嵌套查询、集合查询通常涉及到多个表

同一个题可能可以用这三种方法有不同的做法

3.4.1 单表查询

查询只涉及一个表

输入输出分析法

输入:from子句

输出:select子句

分析:where子句

3.4.1.1 选择表中的若干列

例3.16 查询全体学生的学号与姓名

select Sno, Sname
from Student;

查询全部列

选出所有属性列:

  • 在 select 关键字后面列出所有列名
  • 将<目标列表达式>指定为*

例3.18 查询全体学生的详细记录

select Sno, Sname, Ssex, Sage, Sdept
from Student;

select *
from Student;

查询经过计算的值

select子句的<目标表达式>不仅可以为表中的属性列,也可以是表达式

例3.19 查全体学生姓名及其出生年份

select Sname, 2026 - Sage
from Student;

使用列别名改变查询结果的列标题

select Sname NAME, 'Year of Birth:' BIRTH, 
    2026 - Sage BIRTHDAY, LOWER(Sdebt) DEPARTMENT
from Student;

可以用()确定优先级(例如减法表达式),不过一般sql都能把表达式正常解析

3.4.1.2 选择表中若干元组

消除取值重复的行

如果没有指定 distinct 关键字,则缺省为 all

例3.21 查询选修了课程的学生学号

select all Sno from SC; # 会有重复的
select distinct Sno from SC; # 去重

查询满足条件的元组

1.比较大小

alt text

例3.22 查询计算机科学系全体学生的名单

select name 
from Student
where Sdept='CS';

例3.23 查询所有年龄在20岁以下的学生姓名及其年龄

select name
from Student
where Sage < 20;

例3.24 查询考试成绩有不及格的学生的学号

select distinct Sno # 注意去重
from SC # 注意在学生选课表
where Grade < 60;

2.确定范围

谓词:between... and..., not between... and...

例3.25 查询年龄在20-23岁之间的学生的姓名、系别和年龄

select Sname, Sdept, Sage
from Student
where Sage between 20 and 23;

例3.26 查询年龄不在20-23之间的学生姓名、系别和年龄

select Sname, Sdept, Sage
from Student
where Sage not between 20 and 23;

3.确定集合

谓词:in <值表>, not in <值表>

例3.27 查询计算机科学系(CS)、数学系(MA)和信息系(IS)学生的姓名和性别

select Sname, Sex
from Student
where Sdept in ('CS', 'MA', 'IS');

例3.28 查询既不是计算机科学系、数学系,也不是信息系的学生的姓名和性别

select Sname, Ssex
from Student
where Sdept not in ('IS', 'MA', 'CS');

4.字符匹配

谓词:[not] like '<匹配串>' [escape '<换码字符>']

<匹配串>可以是一个完整的字符串,也可以含有通配符%和_

  • %代表任意长度(长度可以为0)的字符串
  • _代表任意单个字符

Tip

字符匹配,主要是提供了类似正则表达式的通配符规则

例3.30 查询所有姓刘学生的姓名、学号和性别

select Sname, Sno, Ssex
from Student
where Sname like '刘%';

例3.31 查询姓欧阳且全名为三个汉字的学生的姓名

select Sname
from Student
where Sname like '欧阳_';
  • 使用换码字符将通配符转义为普通字符
WHERE column_name LIKE 'pattern' ESCAPE 'escape_character';
  • pattern:匹配模式。
  • escape_character:指定的转义字符,用于标记通配符为普通字符。

例3.34 查询DB_Design课程的课程号和学分

select Cno, Ccredit
from Course
where Cname like 'DB\_Design' escape '\';

5.涉及空值的查询

谓词:is nullis not null

is 不能用等于号代替

例3.36 某些学生选修课程后没有参加考试,所以有选课记录,但没有开始成绩,查询缺少成绩的学生的学号和相应的课程号

select Sno, Cno
from SC
where Grade is null;

例3.37 查所有有成绩的学生学号和课程号

select Sno, Cno
from SC
where Grade is not null;

6.多重条件查询

逻辑运算符:and和or来连接多个查询条件

  • and的优先级高于or
  • 可以用括号改变优先级

例3.38 查询计算机系年龄在20岁以下的学生姓名

select Sname
from Student
where Sdept='CS' and Sage<20;
3.4.1.3 order by 子句
  • order by子句

    • 可以按一个或多个属性列排序
    • 升序:ASC;降序:DESC;缺省为升序
  • 对于空值,排序时显示的次序由具体系统实现来决定,空值跟大头

order by 子句作用就是控制显示的时候以升序或降序形式排列

例3.39 查询选修了3号课程的学生的学号及其成绩,查询结果按分数降序排列

select Sno, Grade
from SC
where Cno='3'
order by Grade desc; # 查询结果按照分数降序排列

例3.40 查询全体学生情况,查询结果按所在系的系号升序排列,同一系中的学生按年龄降序排列

select *
from Student
order by Sdept asc, Sage desc;
3.4.1.4 聚集函数 aggregate function

Warning

聚集函数只能出现在select语句和having子句中

count(*) # 统计元组个数
count([distinct|all] <列名>) # 计算一列中值的个数
sum([distinct|all] <列名>) # 计算一列值的总和(此列必须为数值型)
avg([distinct|all] <列名>) # 一列值的平均值(此列必须为数值型)
max([distinct|all] <列名>) 
min([distinct|all] <列名>) # 求一列中的最大值和最小值

例3.41 查询学生总人数

select count(*)
from Student;

例3.42 查询选修了课程的人数

select count(distinct Sno)
from SC;

计算1号课程的学生平均成绩

select avg(Grade)
from SC
where Cno='1';

例3.45 查询学生201215012选修课程的总学分数

select sum(Ccredit)
from SC, Course
where Sn0='201215012' and SC.Cno=Course.Cno;
3.4.1.5 group by 子句

细化聚集函数的作用对象

  • 如果未对查询结果分组,聚集函数将作用于整个查询结果
  • 对查询结果分组后,聚集函数将分别作用于每个组

  • 按指定的一列或多列值分组,值相等的为一组

group by的作用是控制聚集函数的作用范围

group by执行后,多行会被压缩成一组

SQL 有固定的逻辑执行顺序

\[from \rightarrow where \rightarrow group \ by \rightarrow having \rightarrow select \rightarrow order \ by\]

所以group by执行过后,select后面只能写两类东西:

  1. group by分组列本身
  2. 聚集函数

也就是不可能出现非group by分组列的属性列

例3.46 求各个课程号及相应的选课人数

select Cno, count(Sno)
from SC
group by Cno;

例3.47 查询选修了3门以上课程的学生学号

select Sno
from SC
group by Sno
having count(*) > 3
# 第一次出现 having 子句,其实就起到where的作用group by后面控制每一组的性质要用having

having 和 where 的区别

  • 作用对象不同
  • where 子句作用于基表或视图,从中选择满足条件的元组
  • having短语作用域组,从中选择满足条件的组

Warning

单表查询中的 5 个基础子句讲完了,非常重要,接下来开始多表查询(连接、嵌套、集合)

3.4.2 连接查询

连接查询:同时涉及两个以上的表的查询

连接条件或连接谓词:用来连接两个表的条件

  • [<表名1>.]<列名1> <比较运算符> [<表名2>.]<列名2>
  • [<表名1>.]<列名1> between [<表名2>.]<列名2> and [<表名2>.]<列名3>

  • 连接条件中的歌链接字段类型必须是可比的,但名字不必相同

3.4.2.1 等值与非等值连接查询

等值连接:连接运算符为=

例3.49 查询每个学生及其选修课程的情况

select Student.*, SC.*
from Student, SC
where Student.Sno = SC.Sno;

连接操作的执行过程

  1. 嵌套循环法 nested-loop

    遍历表一的每个元组,遍历表二的每个元组,判断两个元组是否满足连接条件,满足就加入结果表中

  2. 排序合并法 sort-merge

    • 常用与=连接
    • 首先按连接属性对表1和表2排序
    • 遍历表一的每个元组,然后遍历表二的每个元组,由于单调性,找到第一个不满足的就停止遍历
  3. 索引连接 index-join

我们不需要关心这些具体的实现方法

自然连接

记得关系代数里讲连接 \(\bowtie\) 运算的时候讲到了自然连接,而在sql语言中自然连接的实现方式就是把所有的属性列名都写出来,手动去重

select Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
from Student, SC
where Student.Sno = SC.Sno;

一条SQL语句可以同时完成选择和连接查询,这时where子句是由连接谓词和选择谓词组成的复合条件

例3.51 查询选修2号课程且成绩在90分以上的所有学生的学号和姓名

select Student.Sno, Sname
from Student, SC
where Student.Sno=SC.Sno and
      SC.Cno='2' and SC.Grade>90;
3.4.2.2 自身连接
  • 自身连接:一个表语自己连接
  • 需要给表起别名以示区别
  • 由于所有属性名都是同名属性,听次必须使用别名前缀

查询每一门课的间接先修课(即先修课的先修课)

select first.Cno, second.Cpno
from Course FIRST, Course SECOND
where FIRST.Cpno = SECOND.Cno;
3.4.2.3 外连接

外连接与普通连接的区别

  • 普通连接操作只输出满足连接条件的元组
  • 外连接操作以指定表为连接主体,将主体表中不满足连接条件的元组一并输出
  • 左外连接

    列出左边关系中所有的元组

  • 右外连接

    列出右边关系中所有的元组

Tip

外连接可以保留没有匹配上的行,把没匹配上的另一侧补成 NULL

左外连接就会把左表全部保留

例3.53 改写[例3.49]

select Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
from Student left out join SC on 
    (Student.Sno=SC.Sno)
3.4.2.4 多表连接

例3.54 查询每个学生的学号、姓名、选修的课程名及成绩

select Student.Sno, Sname, Cname, Grade
from Student, SC, Course
where Student.Sno = SC.Sno
      and SC.Cno = Course.Cno

3.4.3 嵌套查询

连接查询 VS 嵌套查询

连接查询会有不可避免的笛卡尔积,开销大

  • 一个select-from-where语句称为一个查询块
  • 将一个查询块嵌套在另一个查询块的where子句或having短语的条件中的查询称为嵌套查询
select Sname
from Student
where Sno in (
    select Sno
    drom SC
    where Cno='2'
);

相关子查询

子查询的查询条件依赖于父查询

  • 首先取外层查询中表的第一个元组,根据它与内层查询相关的属性值处理内层查询,若where子句返回值为真,则取此元组放入结果表

  • 然后再取外层表的下一个元组

  • 重复这一过程,直至外层表全部检查完为止

查询每个学生中,成绩高于该学生平均成绩的选课记录

select Sno, Cno, Grade
from SC x
where Grade > (
    select AVG(Grade)
    from SC y
    where y.Sno=x.Sno
);

也是两层for循环的感觉

  • 上层的查询块称为外层查询或父查询
  • 下层查询块称为内层查询或子查询
  • SQL 语言允许多层嵌套查询

    • 即一个子查询中还可以嵌套其他子查询
  • 子查询的限制

    • 不能使用 order by 子句
3.4.3.1 带有in谓词的子查询

例3.55 查询与刘晨在同一个系学习的学生

select Sno, Sname, Sdept
from Student
where Sdept in ( # 这里的in也可以写=,因为只有一个专业
    select Sdept
    from Student
    where Sname = '刘晨'
);

也可以用连接查询做!

select S1.Sno, Sname, Sdept
from Student S1, Student S2
where S1.Sdept = S2.Sdept and
    S2.Sname = '刘晨';

但是连接查询因为有笛卡尔积就会慢一点,但是现代数据库都会优化,所以不存在慢的说法了

3.4.3.2 带有比较运算符的子查询

例3.57 找出每个学生超过他选修课程平均成绩的课程号

select Sno, Cno
from SC x
where Grade >= (
    select avg(Grade) # 这个子查询是一个值
    from SC y
    where y.Sno = x.Sno
);

Tip

相关子查询里,from后面的表起别名后,传入子查询中,就有了for循环的感觉

3.4.3.3 带有any(some)或all谓词的子查询

是一个把集合转换成值的函数

例3.58 查询非计算机科学系中比计算机科学系任意一个学生年龄小的学生姓名和年龄

select Sname, Sage
from Student
where Sage < any(
    select Sage
    from Student
    where Sdept='CS'
) and Sdept <> 'CS';

alt text

3.4.3.4 带有exists谓词的子查询

例3.61 查询没有选修1号课程的学生姓名

select Sname
from Student
where not exists (
    select *
    from SC
    where Sno = Student.Sno and Cno='1'
);

exists 像是对于外部的一条元组写一个if语句,exists后面的内容基本上是和外面的一个子查询有关的,返回的是true or false,还有一种用法是exist里写select 1...where... 如果查询的表不为空就会返回1

3.4.4 集合查询

  • 并操作 union
  • 交操作 intersect
  • 差操作 except

例3.64 查询计算机科学系的学生及年龄不大于19岁的学生

select *
from Student
where Sdept='CS'
union
select *
from Student
where Sage >= 19;

例3.68 查询计算机科学系的学生与年龄不大于19岁的学生的差集

select *
from Student
where Sdept='CS'
except
select *
from Student
where Sage <= 19;

连接、嵌套、集合就是一道题的3种做法

3.4.5 基于派生表的查询

子查询出现在 from 子句中

例3.57 找出每个学生超过他自己选修课程平时成绩的课程号

select Sno, Cno
from SC, (
    select Sno, avg(Grade)
    from SC
    group by Sno
    ) as Avg_sc(avg_sno, avg_grade)
where SC.Sno=Avg_sc.avg_sno
    and SC.grade >= Avg_sc.avg_grade;

3.4.6 select语句的一般形式

select [all|distinct]
    <目标列表达式> [别名] [,<目标列表达式> [别名]]...
from <表名或视图名> [别名]
    [,<表名或视图名> [别名]]...
    | (<select语句>) [as] <别名>
[where <条件表达式>]
[group by <列名1> [having <条件表达式>]]
[order by <列名2> [asc|desc]];

3.5 数据更新

3.5.1 插入数据

insert
into <表名> [(<属性列1>[,<属性列2>...])]
values (<常量1>[,<常量2>]...);

例3.69 将一个新学生元组(学号:201215128;姓名:陈冬;性别:男;所在系:IS;年龄:18岁)插入到Student表中

insert
into Student(Sno, Sname, Ssex, Sdept, Sage)
values('201215128', '陈冬', '男', 'IS', 18);

没有复制的属性列自动赋空值,但还是都显式写出来比较好

3.5.2 插入子查询的结果

insert 
into <表名> [(<属性列1>[,<属性列2>...])]
子查询;
  • select子句目标列必须与into子句匹配

关系数据库管理系统在执行插入语句时会检查所插元组是否破坏表上已定义的完整性规则

  • 实体完整性
  • 参照完整性
  • 用户完整性

    • not null约束
    • unique 约束
    • 值域约束

3.5.2 修改数据

update <表名>
set <列名>=<表达式> [,<列名>=<表达式>]...
[where <条件>];

三种修改方式

  • 修改某一个元组的值

例3.73 将学生201215121的年龄改成22岁

update Student
set Sage=22
where Sno='201215121';
  • 修改多个元组的值

例3.74 将所有学生的年龄增加1

update Student
set Sage = Sage + 1;
  • 带子查询的修改语句

例3.75 将计算机科学系全体学生的成绩置零

update SC
set Grade = 0
where Snp in (
    select Sno
    from Student
    where Sdept = 'CS'
);
  • 关系数据库管理系统在执行修改语句时会检查所插元组是否破坏表上已定义的完整性规则

    • 实体完整性
    • 主码不允许修改
    • 用户定义的完整性

      • not null约束
      • unique约束
      • 值域约束

3.5.3 删除数据

三种删除方式(和修改一样)

  • 删除某一个元组的值
  • 删除多个元组的值
  • 带子查询的删除语句
delete
from <表名>
[where <条件>];
3.5.3.1 删除某一个元组的值

例3.76 删除学号为201215128的学生记录

delete 
from Student
where Sno='201215128';
3.5.3.2 删除多个元组的值

例3.77 删除所有的学生选课记录

delete 
from SC;
注:这是DML

同样效果的: drop Tabel SC;属于DDL

3.5.3.3 带子查询的删除语句

例3.78 删除计算机科学系所有学生的选课记录

delete 
from SC
where Sno in (
    select Sno
    from Student
    where Sdept='CS'
);

3.6 空值的处理

3.6.1 空值的产生

没啥东西

3.6.2 空值的判断

is null is not null

3.6.3 空值的约束条件

属性定义中

  • 有not null约束条件的不能取空值
  • 加了unique限制的属性不能取空值
  • 码属性不能取空值

3.7 视图

虚表

3.7.1 定义视图

视图定义语句里有建立和删除,但修改语句介绍的较少,因为与其修改不如删了重建一个

3.7.1.1 建立视图
create view
    <视图名> [(<列名> [,<列名>]...)]
as <子查询>
[with check option];
  • 基于多个基表的视图

例3.86 建立信息系选修了1号课程的学生的视图(包括学号、姓名、成绩)

create view IS_S1(Sno, Sname, Grade) 
as
select Student.Sno, Sname, Grade
from Student, SC
where Sdept='IS' and 
    Student.Sno=SC.Sno and
    SC.Cno='1';
  • 基于视图的视图

  • 带表达式的视图

  • 分组视图

例3.89 将学生的学号及平均成绩定义为一个视图

create view S_G(Sno, Gavg)
as
select Sno, avg(Grade)
from SC
group by Sno;

例3.90 将Student表中所有女生记录定义为一个视图

create view F_Student(F_Sno, name, sex, age, dept)
as 
select * # 不推荐这么写,最好都写出来
from Student
where Ssex='女';
3.7.1.2 删除视图
drop view <视图名> [CASCADE];

默认是restrict

3.7.2 查询视图

查询视图和查询基本表的操作基本相同,视图就是个表

原理使用的是视图消解法(不关心)

3.7.3 更新视图

例3.95 将信息系学生视图IS_Student中学号“201215122”的学生姓名改为“刘辰”

update IS_Student
set Sname='刘辰'
where Sno='201215122';

Warning

  • 允许对行列自己视图进行更新
  • 对其他类型视图的更新不同系统有不同限制

3.7.4 视图的作用

第四章 数据库的安全性

  • 数据库的安全性是指保护数据库以防止不合法使用所造成的数据泄漏、更改或破坏

  • 系统安全保护措施是否有效是数据库系统主要性能指标之一

4.1 数据库安全性概述

4.1.1 数据库的不安全因素

  1. 黑客、非授权用户
  2. 重要敏感数据泄漏
  3. 安全环境脆弱性

4.1.2 安全标准简介

  • TCSEC

  • CC

4.2 数据库安全性控制(重点)

4.2.1 用户身份鉴别

4.2.2 存取控制

  • 定义用户权限,并将用户权限登记到数据字典中

  • 合法权限检查

组成了数据库管理系统的存取控制系统

常用存取控制方法:

  • 自主存取控制(Discretionary Access Control, DAC)

    C2 级

  • 强制存取控制(Mandatory Access Control, MAC)

    B1 级

4.2.3 自主存取控制方法

通过 SQL 的 GRANT 语句和 REVOKE 语句实现

4.2.4 授权:授予与回收

4.2.4.1 GRANT
GRANT <权限>[,<权限>]...
ON <对象类型><对象名>[,<对象类型><对象名>]...
TO <用户>[,<用户>]...
[WITH GRANT OPTION];

例1 把查询 Student 表权限授给用户 U1

GRANT SELECT
ON TABLE Student
TO U1;

例4 把查询 Student 表和修改学生学号的权限授给用户 U4

GRANT SELECT, UPDATE(Sno) # 缺省括号就是对整个表都有更新权限
ON TABLE Student
TO U4;

例5 把对表 SC 的 INSERT 权限授予 U5用户,并允许他再将此权限授予其他用户

GRANT INSERT
ON TABLE SC
TO U5
WITH GRANT OPTION; # U5 可以传播这个权限
2. REVOKE

REVOKE <权限>[,<权限>]...
ON <对象类型><对象名>[,<对象类型><对象名>]...
FROM <用户>[,<用户>]...[CASCADE | RESTRICT];
授予的权限可以由数据库管理员或其他授权者用 REVOKE 语句收回

CASCADE: 凡是从你这传出去的全给删了

RESTRICT: 如果有传播权限就不让删

把用户 U4 修改学生学号的权限收回

REVOKE UPDATE(Sno)
ON TABLE Student
FROM U4; # FROM PUBLIC 则为收回所有用户的权限
3. 创建数据库模式的权限
CREATE USER <username>
[WITH][DBA|RESOURCE|CONNECT];

CREATE USER 不是 SQL 标准,各个系统实现不一样

  • 如果没有指定创建的新用户的权限,默认该用户拥有 CONNECT 权限。拥有 CONNECT 权限的用户不能创建新用户,不能创建模式,也不能创建基本表,只能登录数据库

  • 拥有 RESOURCE 权限的用户能创建基本表和视图,成为所创建对象的属主。但不能创建模式,不能创建新的用户

  • 拥有 DBA 权限的用户是系统中的超级用户,可以创建新的用户、创建模式、创建基本表和视图等;DBA 拥有对所有数据库对象的存取权限,还可以把这些权限授予一般用户

4.2.5 数据库角色

数据库角色:被命名的一组与数据库操作相关的权限 - 角色是权限的集合

  • 可以为一组具有相同权限的用户创建一个角色

  • 简化授权的过程

  1. 角色的创建

    CREATE ROLE <角色名>
    

  2. 给角色授权

    GRANT <权限>[,<权限>]...
    ON <对象类型>对象名
    TO <角色>[,<角色>]...
    

  3. 将一个角色授予其他的角色或用户

    GRANT <角色1>[,<角色2>]...
    TO <角色3>[,<用户1>]...
    [WITH ADMIN OPTION] # 是否有传递的权限
    

  4. 角色权限的收回

    REVOKE <权限>[,<权限>]...
    ON <对象类型><对象名>
    FROM <角色>[,<角色>]...
    

例4.11 通过角色来实现将一组权限授予一个用户

  1. 首先创建一个角色
    CREATE ROLE R1;
    
  2. 然后 GRANT 语句给角色权限
    GRANT SELECT, UPDATE, INSERT
    ON TABLE Student
    TO R1;
    
  3. 将角色授予用户
    GRANT R1
    TO 张三, 李四;
    
  4. 可以一次性通过 R1 来回收权限
    REVOKE R1
    FROM 张三;
    

4.2.6 强制存取控制方法

更高的安全性,适用政府军事部门

  • 敏感度标记

    • 绝密(Top Secret, TS)
    • 机密(Secret, S)
    • 可信(Confidential, C)
    • 公开(Public, P)
  • 主体的敏感度标记称为许可证级别(Clearance Level)

  • 客体的敏感度标记称为密级(Classification Level)

  • 强制存取控制规则:

    1. 仅当主体的许可证级别大于或等于客体的密级时,该主体才能读取相应的客体

    2. 仅当主体的许可证级别小于或等于客体的密级时,该主体才能相应的客体

强度存取控制(MAC)是对数据本身进行密级标记,无论数据如何复制,标记与数据是一个不可分的整体,只有符合密级标记要求的用户才可以操纵数据

  • 实现强制存取控制时要首先实现自主存取控制

  • 自主存取控制与强制存取控制共同构成数据库管理系统的安全机制

4.3 视图机制

  • 把要保密的数据对无权存取这些数据的用户隐藏起来,对数据提供一定程度的安全保护

  • 间接的实现支持存取谓词的用户权限定义

例4.14 建立计算机系学生的视图,把对该视图的 SELECT 权限授予王平,把该视图上的所有操作权限授予张明

先建立计算机系学生的视图 CS_Student

CREATE VIEW CS_Student
AS 
SELECT *
FROM Student
WHERE Sdept = 'CS';
在视图上进一步定义存取权限
GRANT SELECT
ON CS_Student
TO 王平;
GRANT ALL PRIVILEGES
ON CS_Student
TO 张明;

4.4 审计(Audit)

什么是审计

  • 启用一个专用的审计日志(Audit Log)

    将用户对数据库的所有操作记录在上面

  • 审计延利用审计日志

    监控数据库中的各种行为,找出非法存取数据的人、时间和内容

  • C2 以上安全级别的 DBMS 必须具有审计功能

审计功能的可选性

  • 审计很费时间和空间

  • DBA 可以根据应用对安全性的要求,灵活地打开或关闭审计功能

  • 审计功能主要用于安全性要求较高的部门

  1. 审计事件

    • 服务器事件
      • 审计数据库服务器发生的事件
    • 系统权限

      • 对系统拥有的结构或模式对象进行操作的审计

      • 要求该操作的权限是通过系统权限获得的

    • 语句事件

      • 对 SQL 语句,如 DDL, DML, DQL 及 DCL 语句的审计
    • 模式对象事件

      • 对特定模式对象上进行的 SELECT 或 DML 操作的审计
  2. 审计功能

布拉布拉

  1. AUDIT 语句和 NOAUDIT 语句

    • AUDIT 语句:设计审计功能

    • NOAUDIT 语句:取消审计功能

4.5 数据加密

加密方法:存储加密、传输加密

  • 存储加密

    • 透明存储加密

      数据写到磁盘时对数据进行加密,授权用户读取数据时再对其进行解密

    • 非透明存储加密

      通过多个加密函数实现

  • 传输加密(计网的知识)

    • 链路加密

      在链路层进行加密,传输信息由报头和报文两部分组成,报头和报文均加密

    • 端到端加密

      • 在发送端加密,接受端解密
      • 只加密报文不加密报头
      • 所需密码设备数量相对较少,容易被非法监听者发现并从中获取敏感信息

4.6 其他安全性保护

  • 推理控制

  • 隐蔽信道

  • 数据隐私保护

第五章 数据库完整性

实体完整性,参照完整性,用户定义完整性前面第二章是讲过的,前面讲的都是概念性的东西,这里是讲具体的 SQL 代码

完整性约束条件在讲SQL表的创建也讲过

5.1 实体完整性

主属性不能取空值

5.1.1 实体完整性定义

例5.1 将Student表中的Sno属性定义为码

两种写法:

(1)在列级定义主码

create table Student (
    Sno char(9) primary key, 
    Sname char(20) not null, 
    Ssex char(2), 
    Sage smallint, 
    Sdept char(20)
);

(2)在表级定义主码

create table Student (
    Sno char(9), 
    Sname char(20),
    Ssex char(2),
    Sage smallint,
    Sdept char(20),
    primary key(Sno)
);

更推荐第二种,表级定义,更直观,而且在主码不止一个的时候只能使用表级定义

例5.2 将SC表中的Sno, Cno属性组定义为码

create table SC (
    Sno char(9) not null,
    Cno char(4) not null, 
    Grade smallint,
    primary key(sno, Cno) /*只能在表级定义主码*/
);

5.1.2 实体完整性检查和违约处理

布拉布拉

5.2 参照完整性

5.2.1 参照完整性定义

  • 在create table中用foreign key短语定义哪些列为外码
  • 用references短语指明这些外码参照哪些表的主码

5.2.2 参照完整性检查和违约处理

  • 一个参照完整性将两个表联系了起来
  • 对被参照表和参照表进行增删改操作时可能破坏参照完整性,必须进行检查

处理方法: 1. 拒绝执行

该策略一般设置为默认操作
  1. 级联操作cascade

    删改被操作表的一个元组造成了与参照表的不一致,则删除或修改参照表中的所有造成不一致的元组

  2. 设置为空值null

    删改被操作表的一个元组造成了与参照表的不一致,则将参照表中所有造成不一致的元组的队友属性设置为空值

例5.3 定义Sc中的参照完整性

create table SC (
    Sno char(9) not null,
    Cno char(4) not null,
    Grade smallint,
    primary key (Sno, Cno), //*在表级定义实体完整性*/
    foreign key (Sno) reference Student(Sno), 
    foreign key (Cno) references Course(Cno)
);

例5.4 显式说明参照完整性的违约处理示例

create table SC (
    Sno char(9) not null,
    Cno char(4) not null,
    Grade smallint,
    primary key(Sno) references Student(Sno)
        on delete cascade
        on update cascade
);

5.3 用户定义的完整性

由个人自定

5.3.1 属性上的约束条件

create table时定义属性上的约束条件

  • 列值非空 not null
  • 列值唯一 unique
  • 检查列值是否满足一个条件表达式 check
5.3.1.1 不允许取空值

在定义SC表时,说明Sno, Cno, Grade属性不允许取空值

create table SC (
    Sno char(9) not null,
    Cno char(4) not null,
    Grade smallint not null,
    primary key (Sno, Cno),
    ...
);
5.3.1.2 列值唯一

例5.6 建立部门表DEPT,要求部门名称Dname列取值唯一,部门编号Deptno列为主码

create table DEPT (
    Deptno numeric(2),
    Cname cahr(9) unique not null,
    ...,
    primary key(Deptno)
);
5.3.1.3 用check短语指定列值应该满足的条件

例5.7 Student表的Ssex只允许取‘男’或‘女’

create table Student (
    Sno char(9) primary key,
    Sname char(8) not null,
    Ssex char(2) check (Ssex in ('男', '女')),
    Sage smallint,
    Sdept char(20)
);

这里的check写在一列后面,表示对列的检查

5.3.2 元组上的约束条件

例5.9 当学生性别是男时,其名字不能以Ms.打头

create table Student (
    Sno char(9),
    Sname char(8) not null,
    Ssex char(2),
    Sage smallint,
    Sdept char(20),
    primary key (Sno),
    check (Ssex = '女' or Sname not like 'Ms.%') /*我草有点吊*/
);

5.4 完整性约束命名子句

5.4.1 完整性约束命名子句

constraint <完整性约束条件名> <完整性约束条件>
  • <完整性约束条件>包括not null, uniquem primary key, foreign key, check

alt text

每一列紧贴每一列的约束条件,最后写主码的约束条件

5.4.2 修改表中的完整性限制

使用alter table 语句修改表中的完整性限制,属于DDL语句

例5.12 去掉例5.10 Student表中对性别的限制

alter table Student
drop constraint C4;

*5.5 域中的完整性限制

5.6 断言

create assertion <断言名>
<check 子句>

例5.18 限制数据库课程最多60名学生选修

create assertion ASSE_SC_DB_NUM
check (60 >= (
    select count(*)
    from Course, SC
    where SC.Cno=Course.Cno and 
        Course.Cname='数据库'
));

例5.19 限制一门课最多60名同学选修

create assertion ASSE_SC_CNUM1
check (60>=ALL (
    select count(*)
    from SC
    group by cno    
));

5.7 触发器

Trigger 由用户定义在关系表上的一类由时间驱动的特殊过程

5.7.1 定义触发器

create trigger <触发器名>
{before | after} <触发事件> on <表名>
referencing new|old row as <变量>
for each {row|statement}
[when <触发条件>]<触发动作体>
  • 表的拥有者才可以在表上创建触发器

  • 触发器只能定义在基本表上,不能定义在视图上

  • 基本表的数据发生变化时,将激活定义在该表上相应触发事件的触发器
  • 触发器类型:

    • 行级触发器
    • 语句级触发器

例5.21 当对表SC的Grade属性进行修改时,若分数增加了10%,则将此次操作记录到下面表中:SC_U(Sno, Cno, Oldgrade, Newgrade)

create trigger SC_T
after update of Grade on SC
referencing
    OLD row as OldTuple
    NEW row as NEWTuple
for each row
when (NewTuple.Grade >= 1.1*OldTuple.Grade)
    insert into SC_U(Sno, Cno, OldGrade, NewGrade)
    values(OldTuple.Sno, OldTuple.Cno, OldTuple.Grade, NewTuple.Grade)

5.7.2 激活触发器

5.7.3 删除触发器

第六章 关系数据理论

Tip

第六章关系数据理论,第七章数据库设计,第八章数据库编程

属于数据库的设计与应用开发

前面5章讲的是理论基础

什么样的表可以当数据库的核心数据来用

6.1 问题的提出

关系模式由5部分组成:

这是一个关系模式

\[R(U, D, DOM, F)\]
  • R
  • U 为一组属性
  • D 为属性组U中的属性所来自的域
  • DOM为属性到域的映射
  • F为属性组U上的一组数据依赖

数据库可能有的问题:

  • 数据冗余
  • 增删改异常

为什么会有问题?问题出在数据依赖关系上,不合适的数据依赖关系!

例如在Student(Sno, Sdept, Mname, Cno, Grade)中,Sno, Cno为主码

  • (Sno, Cno, Grade)是一个合适的数据依赖关系,

  • (Sno, Cno, Sdept, Mname)是一个不合适的数据依赖关系(Sdept, Mname仅与Sno有关,与Cno无关)

数据依赖关系有:

  1. 函数依赖 Functional Dependency

y=f(x)一个x对应一个y

  1. 多值依赖 Multi-Valued Dependency

x=f(y)一个x可能对应了多个y

多值依赖是不合适的

所以我们进一步讨论函数依赖关系:

函数依赖关系

  • 完全依赖 vs 部分依赖

    完全依赖是好的,就是缺一不可的关系

    部分依赖(就是有有不止1个主码但是y实际只和其中几个主码有关)不合适,就是Sno, Cno, Sdept, Mname,多此一举

    主要就是看结果由谁决定,必须由所有逐渐一起才能定就是完全依赖,一个主键就能决定的是部分依赖关系

  • 传递依赖

    Cno->专业->系主任 就是一个传递的关系 是不合适的

  • 平凡依赖 vs 非平凡依赖(x能决定y,但y实际和x没有关系)

    废话就是一种平凡依赖,x里已经包含了y,比如张三这个名字里就包含了姓张

    非平凡是合适的

以上为这一章的核心概念,后续都是展开的讲解

总结

一个好的表不能发生插入、删除、更新异常,要尽可能少的数据冗余

引发上述的原因是模式中的某些数据依赖

解决方法: 用规范化理论改造关系模式来消除其中不合适的数据依赖

6.2 规范化 重点重点考试必考

6.2.1 函数依赖

6.2.1.1 函数依赖

y = f(x),称为X函数确定YY函数依赖于X,XY为属性列中的子集

先找候选码,候选码中的每个属性都是主属性

6.2.1.2 平凡函数依赖与非平凡函数依赖

对于 X->Y,表示X能决定Y

平凡的函数依赖: 就是\(Y \subseteq X\),相当于一句废话

不平凡的函数依赖: 就是\(Y \not\subseteq X\)

6.2.1.3 完全函数依赖与部分函数依赖

\(R(U)\)中,如果\(X\rightarrow Y\)并且对于\(X\)的任\(X'\)都有\(X' \nrightarrow Y\),则称Y对X完全函数依赖,记作\(X \xrightarrow{F} Y\)

就是说每个x都是必要的,反之则是不完全依赖,记作\(X \xrightarrow{P} Y\)

只有一个主码时不考虑这个

6.2.1.4 传递函数依赖

\(R(U)\)中,如果\(X \rightarrow Y (Y \nsubseteq X), Y \nrightarrow X, Y \rightarrow Z, Z \nsubseteq Y\),则称Z对X传递函数关系

6.2.2 码

Tip

码很重要,码是第一步,有了码才能看码和非码之间的函数依赖关系

超码

若U部分函数依赖于K,即\(K \xrightarrow{P} U\),则K称为超码Surpkey,候选码是最小的超码(超码就是把主码的限制放宽了,候选码加上任意列就是超码)

候选码(码)

能唯一标识关系中的每一个元组的最小属性集合

鬼话:若\(K \xrightarrow{F} U\),则K称为R的一个候选码(Candidate Key)

若关系模式R有多个候选码,则选定其中一个作为主码(Primary key)

主属性与非主属性

包含在任何一个候选码中的属性,称为主属性

不包含在任何码中的属性称为非主属性,或非码属性

  • 先找候选码
  • 然后分主属性/非主属性

外码

R中属性(组)X不是R的码,但X是另一个关系模式的码,则X是R1的外码

  • 全码:整个属性组是码,称为全码

Tip

假设 R(A, B, C, D, E)中,A和B可以确定,B和C也可以确定

那么(A,B), (B,C)也是候选码

其中挑一个就是主码

A, B, C都是主属性

6.2.3 范式

\[1NF > 2NF > 3NF > BCNF > 4NF > 5NF\]
  • 1 2 3 BC属于函数依赖范畴

  • 4 5属于多值依赖范畴

总结

  • 1NF:单元格里不能是个列表。

  • 2NF:组合主键时,不能有字段只跟着一半主键跑。

  • 3NF:普通字段之间不能互相决定,全都要听主键的。

  • BCNF:任何能决定别人的字段,自己必须有实力当主键

1NF

  • 属性不能是组合属性
  • 不能表里套表

6.2.4 2NF

什么是2NF

在1NF前提下,所有非主属性都完全函数依赖于任何一个候选码

或者反过来,消除掉非主属性对主属性的部分函数依赖就是2NF

6.2.5 3NF

什么是3NF

非主属性对主属性不要有传递函数依赖

就是不要:主属性->主属性/非主属性->非主属性

6.2.6 BCNF

A: 把所有的函数依赖关系列出来,发现有一组箭头左端不是候选码,就不是BCNF

B: 把主属性都列出来,看主属性内部有没有部分和传递依赖关系

不是说关系模式就一定要升得越高越好,范式理论越高,系统效率越低

例6.5 考察关系模式\(C(Cno, Cname, Pcno)\)

  • 只有一个码Cno,没有任何属性对Cno部分依赖或传递依赖,所以\(C \in 3NF\)
  • 同时C中Cno是唯一的决定因素,所以\(C \in BCNF\)
  • 对于关系模式SC(Sno, Cno, Grade)可做同样分析

例6.6 关系模式S(Sno, Sname, Sdept, Sage)

  • 假定Sname也具有唯一性,那么S就有两个码,这两个码都由单个属性组成,彼此不相交
  • 其他属性不存在对码的传递依赖和部分依赖,所以\(S \in 3NF\)
  • 同时S中除Sno,Sname外没有其他决定因素,所以S也属于BCNF

跳跳跳,不想搞这些范式

6.2.7 多值依赖

给定一对(x, z)值,有一组y的值,这组值仅仅决定于x而与z无关

graph TD
    %% 定义节点样式
    classDef default fill:#f9fdfa,stroke:#333,stroke-width:2px;
    classDef nfNode fill:#e1f5fe,stroke:#0288d1,stroke-width:2px,font-weight:bold,font-size:16px;

    %% 定义左侧贯穿的大括号(用子图表示)
    subgraph CoreGoal [大前提:消除决定因素非码的非平凡函数依赖]
        direction TB
        NF1(1NF):::nfNode
        NF2(2NF):::nfNode
        NF3(3NF):::nfNode
        BCNF(BCNF):::nfNode
    end

    NF4(4NF):::nfNode

    %% 定义箭头和文字说明
    NF1 -- "消除非主属性对码的部分函数依赖" --> NF2
    NF2 -- "消除非主属性对码的传递函数依赖" --> NF3
    NF3 -- "消除主属性对码的部分和传递函数依赖" --> BCNF
    BCNF -- "消除非平凡且非函数依赖的多值依赖" --> NF4

    %% 设置子图样式(模拟图中的虚线框和绿色背景)
    style CoreGoal fill:#f2fce8,stroke:#7cb342,stroke-width:2px,stroke-dasharray: 5 5,font-weight:bold

6.2.8 4NF

6.3 数据依赖的公理系统

定义6.11

对于满足一组函数依赖\(F\)的关系模式\(R<U, F>\),其任何一个关系\(r\),若函数依赖\(X \rightarrow Y\)都成立(即\(r\)中任意两元组\(t, s\),若\(t[X] = s[X]\),则\(t[Y] = s[Y]\)),则称逻辑蕴含\(X \rightarrow Y\)

Armstrong 公理系统

  • 一套推理规则,是模式分解算法的理论基础
  • 用途

    • 求给定关系模式的码
    • 从一组函数依赖求得蕴含的函数依赖

\(U\)为属性集总体,\(F\)\(U\)上的一组函数依赖,于是有关系模式\(R<U, F>\)。对\(R<U, F>\)来说有以下的推理规则:

  • A1 自反律:若\(Y \subseteq X \subseteq U\),则\(X \rightarrow Y\)\(F\)所蕴含
  • A2 增广律:若\(X \rightarrow Y\)\(F\)所蕴含,且\(Z \subseteq U\),则\(XZ \rightarrow YZ\)\(F\)所蕴含
  • A3 传递率:若\(X \rightarrow Y\)\(Y \rightarrow Z\)\(F\)所蕴含,则\(X \rightarrow Z\)\(F\)所蕴含

根据A1, A2, A3这3条推理规则可以得到下面三条推理规则

  • 合并规则:
\[由X \rightarrow Y, X \rightarrow Z, 有X \rightarrow YZ\]
  • 伪传递规则
\[由X \rightarrow Y, WY \rightarrow Z, 有XW \rightarrow Z\]
  • 分解规则
\[由X \rightarrow Y及 Z \subseteq Y, 有X \rightarrow Z\]

引理6.1

\(X \rightarrow A_1 A_2 ...A_k成立的充要条件是X \rightarrow A_i 成立(i = 1, 2, ..., k)\)

定义6.12

在关系模式\(R<U, F>\)中为\(F\)所逻辑蕴含的函数依赖的全体叫做\(F\)的闭包,记为\(F^+\)

是函数依赖关系的集合,以F为条件,能得到的所有关系

定义6.13

\(F\)为属性集\(U\)上的一组函数依赖,\(X、Y \subseteq U, X_F^+ = \{ A \mid X \rightarrow A能由F根据Armstrong 公理导出 \}\)\(X_F^+\)称为属性集\(X\)关于函数依赖集\(F\)的闭包

定义6.14

\[如果G^+ = F^+,就说函数依赖集F覆盖G(F是G的覆盖,或G是F的覆盖),或F与G等价\]

两个函数依赖集等价是指他们的闭包等价

6.4 *模式的分解

第七章 数据库设计

7.1 数据库设计概论

7.1.1 数据库设计的特点

7.1.2 数据库设计方法

7.1.3 数据库设计的基本步骤

分6个阶段!

  • 需求分析
  • 概念结构设计 画E-R图
  • 逻辑结构设计
  • 物理结构设计
  • 数据库实施
  • 数据库运行和维护

7.1.4 数据库设计过程中的各级模式

7.2 需求分析

7.3 概念结构设计

7.3.1 概念结构

7.3.2 E-R 模型

7.3.2.1 实体间的联系

两个实体型之间的联系

  • 一对一联系
  • 一对多联系
  • 多对多联系
graph TD
A[实体型A] ---|1/m| B{联系名}
B -- 1/n --- C[实体型B]

两个以上实体型之间的联系

也存在一对一、一对多、多对多的联系

graph TD
A[课程]---|1|B{讲授}
B---|m|C[教师]
B---|n|D[参考书]

单个实体型内的联系

同一个实体集内的各实体之间也可以存在一对一、一对多、多对多的联系

graph TD
A[职工]---|1|B{领导}
B---|n|A

*7.3.3 拓展的E-R模型

*7.3.4 UML

7.3.5 概念结构设计

7.4 逻辑结构设计

7.4.1 E-R图向关系模式的转换

7.4.2 数据模型的优化

7.4.3 设计用户子模式

第八章 数据库编程

SQL 与 高级语言的结合

SQL 擅长处理大规模数据,高度非过程化

高级语言擅长过程,但不擅长处理大规模数据

所以可以把SQL作为函数嵌入到高级语言里

8.4 之后的章节,是考虑不同的部门可能使用的数据库都不是一个企业开发的

8.1 嵌入式 SQL

8.1.1 嵌入式SQL的处理过程

区分主语言和SQL语句,所有SQL语句必须加前缀EXEC SQL

例如在C语言中:

EXEC SQL <SQL语句>;

8.2 过程化 SQL

8.3 存储过程和函数

又返回值的就是函数,否则就是存储过程

8.3.1 存储过程

由过程化SQL书写的过程,经编译和优化后存储在数据库服务器中,使用时只要调用即可

8.3.2 存储过程的用户接口

  1. 创建存储过程
create procedure 过程名([参数1, 参数2, ...]) as <过程化SQL块>;

例8.8 从账户1转指定数额的款项到账户2中

create procedure
transfer (inAccount INT, outAccount INT, amount FLOAT)
/*定义存储过程,和参数*/
as declare /*定义变量*/
    totalDepositOut Float;
    totalDepositIn Float;
    inAccountnum INT;
begin
    select Total into totalDepositOut from Account
    where accountnum=outAccount;
    if totalDepositOut is null then
        rollback; // 如果账户不存在或者账户中五存款就回滚
        return;
    end if;
    if totalDepositOut < amount then
        rollback;
        return;
    end if;
    select Accountnum into inAccountnum from Account
    where accountnum = inAccount;

    if inAccount is null then
        rollback;
        return;
    end if;

    update Account set total=total-amount
    where accountnum=outAccount

    update Account set total=total+amount
    where accountnum=inAccount;

    commit;
end;
  1. 执行存储过程

刚才是写,现在要用了:

call procedure 过程名([参数1, 参数2, ...]);

就跟调用函数一样

  1. 修改存储过程
alter procedure 过程名1 rename to 过程名2;

只能修改名字,不然就重新定义一个

  1. 删除存储过程
drop procedure 过程名();

8.3.3 函数

  1. 函数的定义语句格式
create function 函数名([参数1, 参数2, ...])
returns <类型> as <过程化SQL块>;
  1. 函数的执行语句格式
call/select 函数名([参数1, 参数2, ...]);
  1. 修改函数

重命名:

alter function 过程名1 rename to 过程名2;

重新编译

alter function 过程名 compile;

*8.4 ODBC 编程

*8.5 OLE DB

*8.6 JDBC编程