本文内容系2024年北航软件学院数据管理技术课程课堂笔记,内容大部分取自课程PPT,少量来源于互联网,若有侵权请联系删除。文章内容如有错漏,敬请批评指正!
第六章 完整性约束
08 - 完整性约束
6.1 完整性约束
数据库的完整性:
- 数据库的正确性和相容性
- 防止不合语义的数据进入数据库
6.1.1 完整性控制机制
- 完整性约束条件定义机制
- 完整性约束条件:数据模型的组成部分,约束数据库中数据的语义
- DBMS 应提供定义数据库完整性约束条件的方法
- 完整性检查机制:检查用户发出的操作请求是否违背了完整性约束条件
- 违约反应:如果用户的操作请求使数据违背了完整性约束条件,则采取一定的动作来保证数据的完整性
6.1.2 完整性约束条件
完整性约束条件作用的对象:
- 列:对属性的取值类型、范围、精度等的约束条件
- 元组:对元组中各个属性列间的联系的约束
- 关系:对若干元组间、关系集合上以及关系之间的联系的约束
对象的两种状态:
- 静态:对静态对象的约束是反映数据库状态合理性的约束
- 动态:对动态对象的约束是反映数据库状态变迁的约束。涉及新值和旧值
六类完整性约束条件
-
静态列级约束:对列的取值域的说明,如取值类型、取值范围、是否为空值等约束
-
静态元组约束:
- 规定元组的各个列之间的约束关系
- 如有订货关系(ID,商品,订货量,发货量),要求每条记录中:发货量 < = <= <=订货量
- 用
Check
约束实现
-
静态关系约束:关系的各个元组之间或若干关系之间存在的各种联系或约束
-
分类:实体完整性约束、参照完整性约束、函数依赖约束、统计约束…
-
需用触发器实现
-
例:职工平均工资的2倍 ≤ \;\le\; ≤部门经理的工资 ≤ \;\le\; ≤职工平均工资的5倍
-
-
动态列级约束:是修改列定义或列值时应满足的约束条件
- 修改列定义时的约束:例:将原来允许空值的列改为不允许空值(数据库自动实现)
- 修改列值时的约束:例:年龄只能增长(使用触发器实现)
-
动态元组约束:修改元组值——各个字段之间要满足的约束条件
- 例:职工工资调整不得低于其原来工资 + 工龄 ∗ 1.5 \;*\;1.5 ∗1.5
-
动态关系约束:对关系变化前后状态的限制条件
- 例:事务一致性、原子性等约束条件,先略
最重要的完整性约束条件:
- 域完整性
- 实体完整性约束
- 参照完整性约束
其他完整性约束条件:
- 用户自定义完整性约束,需要编程实现(触发器或check等)
6.2 完整性控制
完整性控制机制:指完整性约束的定义、检查和违约反应规则
-
D(Data) 约束作用的数据对象;
-
O(Operation) 触发完整性检查的数据库操作
- 当用户发出什么操作请求时需要检查该完整性规则:是立即检查还是延迟检查;
-
A(Assertion) 数据对象必须满足的断言或语义约束
- 这是规则的主体;
-
C(Condition) 选择A作用的数据对象值的谓词;
-
P(Procedure) 违反完整性规则时触发的过程。
例:在“学号不能为空”的约束中, D - 约束作用的对象为Sno属性 O - 插入或修改Student 元组时 A - Sno不能为空 C - 无(A可作用于所有记录的Sno属性) P - 拒绝执行该操作
违约反应
关系数据库系统都提供了定义和检查实体完整性、参照完整性和用户定义的完整性的功能
违反实体完整性规则的操作一般是拒绝执行
违反参照完整性的操作的违约反应:
- 可以拒绝执行
- 也可以接受这个操作,同时执行一些附加的操作,以保证数据库的状态正确
参照完整性违约反应
- 外键是否可以取空值:依赖应用环境语义而定
- 在被参照关系中删除元组时,若参照关系有若干元组的外键值与被删除的被参照关系的主键值相同,此时的违约反应可以是:
- 级联删除(CASCADES)
- 受限删除(RESTRICTED)
- 置空值删除(NULLIFIES)
用户自定义约束的实现
Check约束
通过限制输入到列中的值来强制域的完整性,常用于实现静态列级约束和静态元组约束。
触发器
-
用于实现复杂逻辑的用户自定义约束的工具
-
是一种特殊的存储过程
- 与表(或视图、数据库等)紧密相连,不能脱离宿主存在
- 由数据库自动调用执行,用户不能调用
- 没有参数和返回值
-
是一种
Event-Condition-Action
规则的实现- Event - 事件: 引起数据库更新的事件,如执行Insert语句向表中插入数据
- Condition - 条件: 判断是否满足条件的SQL表达式
- Action - 动作: 任意的SQL语句
CREATE [ OR REPLACE ] TRIGGER trigger_name [BEFORE | AFTER | INSTEAD OF] trigger_event ON table_reference [FOR EACH ROW [ WHEN trigger_condition ] ] trigger_body;
-
示例:Fruits ( Fname, Price );Sells ( Cname, Fname, Stime, Quantity )
- 需求: 当向
Sell
表中插入一条新的购买记录的时候,若Fname
(水果名称)在fruits
表中不存在,则向Fruits
表中插入一条新的记录,其中Fname
为新购买记录中的水果名称,Price
为NULL
。
CREATE TRIGGER FruitTrig AFTER INSERT ON Sells # 事件 REFERENCING NEW ROW AS NewTuple FOR EACH ROW WHEN (NewTuple.Fname NOT IN (SELECT Fname FROM Fruits)) # 条件 INSERT INTO Fruits(Fname) VALUES(NewTuple.Fname); # 动作
- 需求: 当向
事件:
- 表/视图级事件:
INSERT / DELETE / UPDATE
- 数据库级事件:
CREATE / ALTER / DROP TABLE / PROCEDURE / VIEW…
触发的时机:
AFTER / BEFORE / INSTEAD OF
Instead Of
触发器会替换掉用户提交的操作语句。常用于不可更新视图的更新。(不用用户操作,改用触发器语句)触发/执行粒度
- 语句级触发器:缺省
- 每条用户语句触发一次
- ⭐️语句级触发器不能引用新值和旧值
- 行级触发器:
FOR EACH ROW
- 对于表中每条受影响记录,触发一次触发器
Referencing
引用
- 名为
Inserted
的表,包含新插入的行- 名为
Deleted
的表,包含删除的行- 在触发器业务逻辑中可以通过引用来处理新值、旧值
- OpenGauss、MySQL等数据库中可以直接使用关键字
New
、Old
来引用新、旧值:New.Attribute
,Old.Attribute
执行触发器需要commit(提交)
- 动作
- 实现自定义约束的业务逻辑
- 通常用于进行关联数据更新,不能返回查询结果
- 若需多于一条语句,则可用程序块实现:
Begin…End
- 自学存储过程编程技术
- 拒绝更新的违约动作:引发系统错误,提示数据库拒绝更新,可用
RaiseError
(SQLServer)、Signal
(MySQL)、Raise
(OpenGauss)函数Instead of
触发器
- 将用户提交的语句替换成触发器代码
- 在视图上创建的
Instead of
触发器可以实现不可更新视图的更新