5数据库完整性

第五章 数据库完整性

5.1 数据库完整性概述

1. 数据库完整性的定义

数据库的完整性 是指数据库中存储的数据在任何时刻都必须是正确有效相容的。

  • 数据的正确性 :指数据必须符合现实世界的语义,准确反映当前实际状况。
    • 示例性别 属性的域只能是 '男' 或 '女';成绩 属性的域必须在 [0, 100] 区间内;学号 属性必须唯一。
  • 数据的相容性 :指数据库中同一对象在不同关系表中的数据必须符合逻辑,保持一致。
    • 示例 :学生所选课程的 课程号 必须是课程表中已存在的 课程号;学生所属院系的 院系号 必须是院系表中已存在的 院系号

2. 完整性与安全性的辨析

完整性和安全性是两个截然不同的概念,必须严格区分:

维度 数据完整性 数据安全性
目标 防止数据库中存在不符合语义的、不正确的数据。 保护数据库,防止恶意的破坏和非法的存取。
防范对象 不合语义的、不正确的数据。 非法用户和非法操作。
核心机制 阻止合法用户 通过合法操作 向数据库中加入不正确的数据 防范非法用户非法操作 存取数据库中的正确数据

3. 完整性控制机制

为维护数据库完整性,数据库管理系统 (DBMS) 必须提供一套完整的机制:

  1. 完整性约束定义:提供定义完整性约束条件(或称完整性规则)的机制。SQL 标准支持通过数据定义语言 (DDL) 来实现。
  2. **完整性检查:**提供在 INSERTUPDATEDELETE 等数据操纵操作执行后,或在事务提交时,自动检查数据是否违背完整性约束的机制。
  3. 违约处理:当系统发现用户的操作违背了完整性约束时,必须采取预定义的动作(如拒绝执行、级联操作)来保证数据的完整性。

DBMS 实现完整性控制的优势在于:

  • 减轻了应用程序员的负担,约束条件无需在应用程序中逐一实现。
  • 提供了全局一致的完整性保护,避免了因应用逻辑疏忽导致的漏洞。

5.2 实体完整性

1. 实体完整性的定义

关系模型的实体完整性规则要求:基本关系的主码属性值不能为空 (NULL),且必须唯一。

SQL 实现 :在 CREATE TABLE 语句中通过 PRIMARY KEY 关键字定义。

  • 单属性主码:可在列级或表级定义。

    • [例 5.1] Student 表的 Sno 为主码

      • (1) 列级约束:

        sql 复制代码
        CREATE TABLE Student (
            Sno CHAR(8) PRIMARY KEY,
            Sname CHAR(20) NOT NULL,
            Ssex CHAR(6),
            Sbirthdate Date,
            Smajor VARCHAR(40)
        );
      • (2) 表级约束:

        sql 复制代码
        CREATE TABLE Student (
            Sno CHAR(8),
            Sname CHAR(20) NOT NULL,
            Ssex CHAR(6),
            Sbirthdate Date,
            Smajor VARCHAR(40),
            PRIMARY KEY (Sno)
        );
  • 多属性(组合)主码必须在表级定义。

    • [例 5.2] SC 表的 (Sno, Cno) 为主码

      sql 复制代码
      CREATE TABLE SC (
          Sno CHAR(8) NOT NULL,
          Cno CHAR(5) NOT NULL,
          Grade SMALLINT,
      	Semester CHAR(5),
      	Teachingclass CHAR(8),
          PRIMARY KEY (Sno, Cno) /* 只能在表级定义 */
      );

      注:组合主码的各属性必须显式或隐式地声明为 NOT NULL

2. 实体完整性检查与违约处理

  • 检查时机 :在 INSERT 操作或对主码列执行 UPDATE 操作时。
  • 检查内容
    1. 非空检查 :检查主码的各个属性是否为 NULL。若任一属性为空,操作被拒绝。
    2. 唯一性检查:检查主码值是否与表中已有元组的主码值重复。若重复,操作被拒绝。
  • 违约处理拒绝 (REJECT) 执行该数据操纵操作。
  • 检查实现 :为避免全表扫描(效率极低),RDBMS 通常会在主码上自动创建一个 UNIQUE 索引 (如 B+ 树索引),以高效地完成唯一性检查。

5.3 参照完整性

1. 参照完整性的定义

  • 关系模型的参照完整性规则定义了关系与关系之间的联系 。规则要求:
    • 若属性(或属性组)FFF 是基本关系 RRR(称为参照关系从表 )的外码 ,它与基本关系 SSS(称为被参照关系主表 )的主码 KKK 相对应,则对于 RRR 中每个元组,其 FFF 上的值必须满足以下条件之一:
      1. 取空值 (NULL)(前提是 F 的所有属性均允许为空)。
      2. 等于 S 中某个元组的主码 K 的值(即该外码值必须在被参照表中存在)。

SQL 实现 :在 CREATE TABLE 语句中通过 FOREIGN KEYREFERENCES 短语定义。

  • [例] Student 表的 Smajor 参照 MAJOR 表的 Majorno

    sql 复制代码
    CREATE TABLE Student (
        Sno CHAR(8) PRIMARY KEY,
        ...
        Smajor VARCHAR(40),
        /* 表级定义参照完整性 */
        FOREIGN KEY (Smajor) REFERENCES MAJOR(Majorno)
    );
  • [例 5.3] SC 表的 Sno 参照 StudentCno 参照 Course

    sql 复制代码
    CREATE TABLE SC (
        Sno CHAR(8) NOT NULL,
        Cno CHAR(5) NOT NULL,
        ...
        PRIMARY KEY (Sno, Cno),
        /* 定义两个外码约束 */
        FOREIGN KEY (Sno) REFERENCES Student(Sno),
        FOREIGN KEY (Cno) REFERENCES Course(Cno)
    );

2. 参照完整性检查与违约处理

当对参照表或被参照表执行 INSERTUPDATEDELETE 操作时,均可能破坏参照完整性,系统必须进行检查。

可能破坏完整性的情况

  1. 参照表 (SC) INSERT / UPDATE:导致外码值 (Sno) 在被参照表 (Student) 中不存在。
  2. 被参照表 (Student) DELETE:导致参照表 (SC) 中仍存在参照该主码的外码。
  3. 被参照表 (Student) UPDATE (主码):导致参照表 (SC) 中仍存在参照旧主码的外码。

违约处理策略 :可在定义外码时显式声明 ON DELETEON UPDATE 的动作。

  1. 拒绝执行
    • 不允许执行该操作。这是 SQL 标准中的默认策略
  2. 级联操作
    • ON DELETE CASCADE:删除被参照元组时,级联删除所有参照该元组的元组。
    • ON UPDATE CASCADE:更新被参照元组的主码时,级联更新所有参照该元组的外码值。
  3. 设置为空值
    • ON DELETE SET-NULL:删除被参照元组时,将所有参照该元组的外码值设置为 NULL
    • ON UPDATE SET-NULL:更新被参照元组的主码时,将所有参照该元组的外码值设置为 NULL

重要约束 : 如果外码是参照表主码的组成部分(如 SC 表中的 SnoCno),则根据实体完整性,这些属性不允许为空 。因此,针对它们的外码约束不能选择 SET-NULL 策略。

[例 5.4] 显式声明违约处理策略:

sql 复制代码
CREATE TABLE SC (
    Sno CHAR(8) NOT NULL,
    Cno CHAR(5) NOT NULL,
    ...
    PRIMARY KEY (Sno, Cno),
    FOREIGN KEY (Sno) REFERENCES Student(Sno)
        ON DELETE CASCADE  /* 删除学生时,级联删除其选课记录 */
        ON UPDATE CASCADE, /* 更新学号时,级联更新其选课记录 */
    FOREIGN KEY (Cno) REFERENCES Course(Cno)
        ON DELETE NO ACTION /* 删除课程时,若有学生选课则拒绝 */
        ON UPDATE CASCADE   /* 更新课程号时,级联更新SC表 */
);

5.4 用户定义的完整性

指针对特定应用领域的需求,由用户自行定义的数据语义约束。

1. 属性上的约束条件

定义在单个属性列上的约束,在 CREATE TABLE 时定义。

  1. 列值非空

    sql 复制代码
    CREATE TABLE SC
    ( 
        Sno CHAR(8) NOT NULL,
     	Cno CHAR(5) NOT NULL,
        Grade SMALLINT NOT NULL,
        Semester CHAR(5),
        TeachingclassCHAR(8),
        PRIMARY KEY (Sno, cno),
        ...
  2. 列值唯一

    sql 复制代码
    CREATE TABLE School
    (
        Schoolno CHAR(8),
        SchooIname VARCHAR(40) UNIQUE NOT NULL,  /*要求Schoolname列值唯一,并且不能取空值*/
        PRIMARY KEY (Schoolno)
    );
  3. CHECK 短语:指定列值必须满足的布尔表达式。

    • [例 5.7] Ssex CHAR(6) CHECK (Ssex IN ('男','女'))

    • [例 5.8] Grade SMALLINT CHECK (Grade >= 0 AND Grade <= 100)

违约处理 :当插入或修改数据不满足上述约束时,操作被拒绝执行

2. 元组上的约束条件

定义涉及同一元组中多个属性之间相互关系的约束。通常在表级使用 CHECK 短语定义。

  • [例 5.9] 当学生性别为男时,其名字 (Sname) 不能以 'Ms.' 开头。

    sql 复制代码
    CREATE TABLE Student (
        ...
        Sname CHAR(20) NOT NULL,
        Ssex CHAR(6),
        ...
        PRIMARY KEY (Sno),
        /* 定义元组级CHECK约束 */
        CHECK (Ssex='女' OR Sname NOT LIKE 'Ms.%')
    );
    • 逻辑分析 :若 Ssex='女',约束为真。若 Ssex='男',则必须满足 Sname NOT LIKE 'Ms.%' 约束才为真。

违约处理 :当插入或修改元组导致 CHECK 约束的谓词计算结果为 FALSE 时,操作被拒绝执行

5.5 完整性约束命名字句

为了便于后续对完整性约束进行管理(如修改或删除),可以使用 CONSTRAINT 子句为约束命名。

1. 约束命名语法

sql 复制代码
CONSTRAINT <完整性约束条件名> <完整性约束条件>
  • Student 表的多个约束命名:

    sql 复制代码
    /*建立学生登记表Student,要求学号在10000000~29999999之间,姓名不能取空值,性别只能是 "男" 或 "女" ,出生日期在1980之后,学号是主码。*/
    CREATE TABLE Student (
        Sno CHAR(8)     CONSTRAINT C1 CHECK (Sno BETWEEN '10000000' AND '29999999'),
        Sname CHAR(20)  CONSTRAINT C2 NOT NULL,
        Ssex CHAR(6)    CONSTRAINT C3 CHECK (Ssex IN ('男','女')),
        Sbirthdate Date CONSTRAINT C4 CHECK (Sbirthdate > '1980-1-1'),
        Smajor VARCHAR(40),
        CONSTRAINT StudentKey PRIMARY KEY(Sno)
    );
  • TEACHER 表的元组约束命名:

    sql 复制代码
    /*建立教师表TEACHER,要求每个教师的应发工资不低于3000元。应发工资是工资列Sal与扣除项Deduct之和。*/
    CREATE TABLE TEACHER
    (
        Eno CHAR(8) PRIMARY KEY,/*在列级定义主码*/
    	Ename VARCHAR(20),
        JobCHAR(8),
        Sal NUMERIC(7,2),
        Deduct NUMERIC(7,2),
        Schoolno CHAR(8),
        CONSTRAINT TEACHERFKey FOREIGN KEY (Schoolno) REFERENCES School(Schoolno),
        CONSTRAINT C1 CHECK (Sal+ Deduct >= 3000)
    );

2. 修改表中的完整性限制

使用 ALTER TABLE 语句来删除或增加约束。

  • 删除约束
    • ALTER TABLE Student DROP CONSTRAINT C3;
  • 增加约束
    • ALTER TABLE Student ADD CONSTRAINT <约束名> <约束定义>;
  • 修改约束
    • SQL 标准不支持 直接修改约束。通常通过先删除旧约束 ,再添加新约束 来实现。

5.7 触发器

  • 触发器 是一种由事件驱动的特殊过程,定义在关系表上。当特定的数据操纵事件(INSERT, UPDATE, DELETE)发生时,触发器被自动激活,执行预定义的动作。

  • 触发器提供了比 CHECK 约束更复杂、更精细的完整性控制能力,它遵循 ECA 规则

1. 定义触发器

语法结构

sql 复制代码
CREATE TRIGGER <触发器名>
{ BEFORE | AFTER } <触发事件> ON <表名>
[ REFERENCING ( NEW | OLD ) ( ROW | TABLE ) AS <变量名> ]
[ FOR EACH { ROW | STATEMENT } ]
[ WHEN ( <触发条件> ) ]<触发动作体>
  • 触发时机 (BEFORE | AFTER)BEFORE(动作执行前)或 AFTER(动作执行后)。
  • 触发事件 (INSERT | UPDATE | DELETE):激活触发器的 DML 操作。
  • 触发对象 (ON <表名>):触发器必须定义在基本表上。
  • 触发类型 (ROW | STATEMENT)
    • 语句级 (FOR EACH STATEMENT):操作执行一次,触发器激活一次(无论影响多少行)。
    • 行级 (FOR EACH ROW):操作影响几行,触发器激活几次。
  • 参照变量 (REFERENCING)
    • OLD ROW AS OldTuple:引用行级操作前的旧值。
    • NEW ROW AS NewTuple:引用行级操作后的新值。
    • NEW TABLE AS DELTA:引用语句级 INSERT 后的新值集合。
  • 触发条件 (WHEN):可选。只有当该条件为真时,触发动作体才执行。
  • 触发动作体 (Trigger Body):一个 SQL 过程块(如 PL/SQL)。

示例

  • [例 5.21] 行级 AFTER 触发器 :当 SC 表的 Grade 更新且增幅超过 10% 时,记录日志。

    sql 复制代码
    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)
    BEGIN
        INSERT INTO SC_U(Sno, Cno, OldGrade, NewGrade)
        VALUES(OldTuple.Sno, OldTuple.Cno, OldTuple.Grade, NewTuple.Grade);
    END;
  • [例 5.23] 行级 BEFORE 触发器 :实现 CHECK 无法完成的违约处理(自动修改)。教授工资若低于 4000,自动设为 4000。

    sql 复制代码
    CREATE TRIGGER Insert_Or_Update_Sal
    BEFORE INSERT OR UPDATE ON Teacher
    REFERENCING NEW row AS newtuple
    FOR EACH ROW
    BEGIN
        IF (newtuple.Job = '教授') AND (newtuple.Sal < 4000)
        THEN newtuple.Sal := 4000;
        END IF;
    END;
  • 语句级 AFTER 触发器 :统计 Student 表每次插入的学生人数。

    sql 复制代码
    CREATE TRIGGER Student_Count
    AFTER INSERT ON Student
    REFERENCING NEW TABLE AS DELTA
    FOR EACH STATEMENT
    INSERT INTO StudentInsertLog (Numbers)
    SELECT COUNT(*) FROM DELTA;

2. 激活触发器

触发器由数据库服务器在响应触发事件时自动执行。若一个表上定义了多个触发器,执行遵循以下顺序:

  1. 执行所有 BEFORE 触发器。
  2. 执行激活触发器的 SQL 语句 (DML)。
  3. 执行所有 AFTER 触发器。

3. 删除触发器

sql 复制代码
DROP TRIGGER <触发器名> ON <表名>;
 Student_Count
  AFTER INSERT ON Student
  REFERENCING NEW TABLE AS DELTA
  FOR EACH STATEMENT
  INSERT INTO StudentInsertLog (Numbers)
  SELECT COUNT(*) FROM DELTA;

2. 激活触发器

触发器由数据库服务器在响应触发事件时自动执行。若一个表上定义了多个触发器,执行遵循以下顺序:

  1. 执行所有 BEFORE 触发器。
  2. 执行激活触发器的 SQL 语句 (DML)。
  3. 执行所有 AFTER 触发器。

3. 删除触发器

sql 复制代码
DROP TRIGGER <触发器名> ON <表名>;
相关推荐
汽车仪器仪表相关领域2 小时前
ZDT-I 伺服电机测试系统
数据库·功能测试·安全·机器人·压力测试·可用性测试
天竺鼠不该去劝架2 小时前
2026 金融智能体选型逻辑:懂金融、重合规、能落地
大数据·数据库·人工智能
煎蛋学姐2 小时前
SSM校园二手物品交易网站n131p(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·计算机毕设·ssm 框架·商品类别管理
悟能不能悟2 小时前
Oracle 如何查找function信息
数据库·oracle
用户47949283569152 小时前
Superset 展示 JSONB 中文变天书?真相竟然是 Python 的“过度保护”
数据库·后端
ID_180079054732 小时前
Python采集闲鱼商品详情API:JSON数据解析与应用实践
数据库·python·json
白帽子凯哥哥2 小时前
2026零基础如何参与护网行动?(非常详细)
数据库·sql·学习·漏洞·xss
我爱学习好爱好爱3 小时前
Prometheus监控栈 监控Springboot2+Vue3+redis项目
数据库·redis·prometheus
高一要励志成为佬3 小时前
【数据库】第一章:绪论
数据库