数据库完整性

一、核心概念

(一)数据库完整性定义
  • 目标 :确保数据的正确性 (符合语义约束)和相容性(数据间逻辑一致),防止无效数据输入输出(如 "垃圾进垃圾出" 问题)。
  • 与安全性区别:
    • 完整性:关注数据本身的语义合法性,防止错误数据。
    • 安全性:关注数据的访问控制,防止恶意破坏和非法存取。
(二)完整性约束条件
  • 作用对象:列(属性)、元组(记录)、关系(表)。

  • 分类:

    类型 对象 说明
    静态列级约束 限制列的取值范围(如数据类型、格式、值域、非空等)。
    静态元组约束 元组 元组内各列之间的逻辑关系(如年龄 > 0 且工资 > 年龄 ×100)。
    静态关系约束 关系 表间或表内元组的关联约束(如实体完整性、参照完整性、函数依赖)。
    动态列级约束 修改列定义或值时的约束(如修改列类型时需保证数据兼容)。
    动态元组约束 元组 元组值修改前后的逻辑关系(如工资调整后不能低于原工资的 90%)。
    动态关系约束 关系 表状态变化的约束(如事务的原子性、一致性)。

二、完整性控制机制

(一)DBMS 的核心功能
  1. 定义功能 :通过 DDL 语句(如CREATE TABLE)声明完整性约束。
  2. 检查功能:在数据插入、更新、删除时触发约束检查。
  3. 处理功能:若违反约束,执行预设动作(如拒绝操作、级联更新、自动修复数据)。
(二)约束检查方式
  • 立即执行约束:操作完成后立即检查(默认方式)。
  • 延迟执行约束:事务提交前检查(适用于跨表或跨事务的约束)。
(三)完整性规则模型
  • 五元组表示

    复制代码
    (D, O, A, C, P)
    • D:约束作用的数据对象(如列、表)。
    • O:触发约束的操作(如INSERTUPDATE)。
    • A:约束条件(如 "工资≥1000")。
    • C:筛选数据的谓词(如 "职称 ='教授'")。
    • P:违反约束时的处理过程(如拒绝操作、设置默认值)。

示例

  • 约束

    :教授工资不得低于 1000 元。

    • D:工资列(Sal)
    • OUPDATE操作
    • A:Sal ≥ 1000
    • C:职称 ='教授'
    • P:拒绝执行更新

三、典型完整性约束实现

(一)实体完整性
  • 目标:确保表中每一行数据唯一标识(通过主码实现)。

  • SQL 实现

    (Oracle 示例):

    sql 复制代码
    CREATE TABLE Student (
      Sno NUMBER(8) CONSTRAINT PK_SNO PRIMARY KEY, -- 单字段主码
      Sname VARCHAR(20),
      Sage NUMBER(20)
    );
    
    CREATE TABLE SC (
      Sno NUMBER(8),
      Cno NUMBER(2),
      Grade NUMBER(2),
      CONSTRAINT PK_SC PRIMARY KEY(Sno, Cno) -- 联合主码
    );
(二)参照完整性
  • 目标:维护表间关联数据的一致性(通过外码实现)。

  • 关键问题:

    • 外码是否允许为空值?
    • 被参照表删除 / 更新主码时的级联操作(如CASCADERESTRICTSET NULL)。
  • SQL 实现

    (Oracle 示例):

    sql 复制代码
    CREATE TABLE EMP (
      Empno NUMBER(4),
      Deptno NUMBER(2),
      CONSTRAINT FK_DEPTNO FOREIGN KEY(Deptno)
      REFERENCES DEPT(Deptno) ON DELETE CASCADE -- 级联删除
    );
(三)用户定义完整性
  • 目标:自定义业务规则(如值域、格式、逻辑运算约束)。

  • SQL 实现:

    1. 非空约束(NOT NULL):

      sql 复制代码
      CREATE TABLE Student (
        Sname VARCHAR(20) CONSTRAINT C2 NOT NULL -- 姓名非空
      );
    2. 唯一约束(UNIQUE):

      sql 复制代码
      CREATE TABLE DEPT (
        Dname VARCHAR(9) CONSTRAINT U1 UNIQUE -- 部门名称唯一
      );
    3. 检查约束(CHECK):

      sql 复制代码
      CREATE TABLE Student (
        Sno NUMBER(5) CONSTRAINT C1 CHECK(Sno BETWEEN 90000 AND 99999), -- 学号范围
        Ssex VARCHAR(2) CONSTRAINT C4 CHECK(Ssex IN('男','女')) -- 性别枚举
      );
    4. 基于表达式的约束:

      sql 复制代码
      CREATE TABLE EMP (
        Sal NUMBER(7,2),
        Deduct NUMBER(7,2),
        CONSTRAINT C1 CHECK(Sal + Deduct <= 3000) -- 应发工资上限
      );

四、高级实现:触发器(Trigger)

  • 作用:对复杂业务规则(如跨表约束、自动数据修复)进行编程控制。

  • 示例

    :教授工资低于 4000 元时自动调整为 4000 元(Oracle PL/SQL):

    sql 复制代码
    CREATE TRIGGER UPDATE_SAL
    BEFORE INSERT OR UPDATE OF Sal, Pos ON Teacher
    FOR EACH ROW
    WHEN (NEW.Pos = '教授')
    BEGIN
      IF NEW.Sal < 4000 THEN
        NEW.Sal := 4000; -- 自动修复数据
      END IF;
    END;

五、总结

  • 核心原则:通过多层约束(列级、元组级、关系级)和动态机制(触发器)确保数据符合业务语义。
  • 实践建议:
    • 优先使用 DBMS 内置约束(如PRIMARY KEYCHECK)实现简单规则。
    • 复杂规则通过触发器或应用层逻辑实现,避免过度依赖数据库层。
    • 结合事务机制(如COMMIT/ROLLBACK)处理延迟约束,保证数据一致性。

第八章 关系数据理论

一、核心理论与问题引入

(一)关系模式与数据依赖
  • 关系模式定义
    形式化表示为 R(U, F),其中 U 是属性集合,F 是函数依赖集合。例如,学生选课关系 SC(Sno, Cno, Grade) 中,F = {(Sno, Cno)→Grade}
  • 数据依赖类型:
    • 函数依赖(FD) :如 Sno→Sdept(学号决定所在系)。
    • 多值依赖(MVD):后续章节深入,本章以函数依赖为主。
(二)不良模式的四大问题(以学生 - 系 - 课程表为例)
问题类型 具体表现
数据冗余 系主任姓名重复存储(如计算机系的 "张明" 重复出现在所有该系学生记录中)。
更新异常 修改系主任时需更新所有该系学生记录,漏改会导致数据不一致。
插入异常 新系无学生时无法插入系信息(主码 (Sno, Cno) 不能为空)。
删除异常 删除最后一个学生记录时,系信息也被删除("级联删除" 副作用)。

解决思路 :通过模式分解 将一个 "大而全" 的关系拆分为多个 "小而精" 的关系,如拆分为 Student(Sno, Sdept)Dept(Sdept, Mname)SC(Sno, Cno, Grade)

二、核心概念:码与函数依赖

(一)码的分类与判定
  1. 候选码:

    • 唯一标识元组最小化 的属性组,如 SC(Sno, Cno) 中的 (Sno, Cno)
  2. 判定原则:

    1. 出现在所有函数依赖左部的属性必为主属性(如 SnoSno→Sdept 左部,是主属性)。
    2. 不在任何函数依赖中的属性必包含在候选码中(全码场景,如 R(P, W, A) 中无函数依赖,全属性组为码)。
    3. U 中只出现在F中函数依赖的右部的属性,一定不是主属性,不出现在任何码中。
  3. 主码 :从候选码中选定的一个,如学生表选 Sno 为主码。

  4. 外码 :其他表的主码,如 SC 中的 SnoStudent 表的主码,故为外码。

(二)函数依赖的类型
  1. 完全函数依赖:

    • 记为 X →^F Y,如 (Sno, Cno)→Grade(单个 SnoCno 无法决定 Grade)。
  2. 部分函数依赖

    • 记为 X →^P Y,如 (Sno, Cno)→Sdept(仅 Sno 即可决定 Sdept)。
  3. 传递函数依赖

    • Sno→Sdept→Mname,则 Sno→^t Mname 是传递依赖(Sdept 是中间属性)。

例题:

考虑某商业集团数据库中的关系模式:销售(商店编号,商品编号,库存数量,部门编号,负责人)。若规定:①每个商店的每种商品只在一个部门销售;②每个商店的每个部门只有一个负责人;③每个商店的每种商品只有一个库存数量。

解析:

•根据语义,此关系模式满足的函数依赖为:

① (商店编号,商品编号)→部门编号

② (商店编号,部门编号)→负责人

③ (商店编号,商品编号)→库存数量

•由于商店编号和商品编号只出现在函数依赖的左部,码中必含有这两个属性。且(商店编号,商品编号)能够完全函数决定该关系的所有属性,因此候选码(也是主码)为:(商店编号,商品编号)。

•主属性包括:商店编号和商品编号。

非主属性包括:部门编号、负责人和库存数量。

三、范式分解:从 1NF 到 BCNF

(一)范式层级与判定标准
范式 核心要求 解决的问题
1NF 属性不可再分(如 "地址" 不能包含省、市、区多列)。 基础格式规范
2NF 消除非主属性对码的部分依赖(如拆 S-L-C(Sno, Cno, Sdept, Sloc, Grade)SCS-L)。 部分依赖导致的冗余
3NF 消除非主属性对码的传递依赖(如拆 S-L(Sno, Sdept, Sloc)S-DD-L)。 传递依赖导致的冗余
BCNF 消除主属性对码的不良依赖(如 STJ(S, T, J)T→J 需拆分为 STTJ)。 主属性间的依赖异常
(二)分解步骤与示例
1. 从非 2NF 到 2NF:消除部分依赖

案例 :关系模式 S-L-C(Sno, Cno, Sdept, Sloc, Grade)

  • 函数依赖
    (Sno, Cno)→^F GradeSno→^P SdeptSno→^P SlocSdept→Sloc)。

  • 分解步骤:

    • 保留完全依赖:SC(Sno, Cno, Grade)
    • 提取部分依赖:S-L(Sno, Sdept, Sloc)
  • 结果SC∈2NFS-L∈2NF(但 S-L 仍存在传递依赖,需进一步分解)。

2. 从 2NF 到 3NF:消除传递依赖

案例 :关系模式 S-L(Sno, Sdept, Sloc)

  • 函数依赖Sno→Sdept→SlocSno→Sloc 是传递依赖)。
  • 分解步骤:
    • 保留直接依赖:S-D(Sno, Sdept)
    • 提取传递依赖:D-L(Sdept, Sloc)
  • 结果S-D∈3NFD-L∈3NF
3. 从 3NF 到 BCNF:消除主属性依赖

案例 :关系模式 STJ(S, T, J)(学生 - 教师 - 课程,T→J

  • 函数依赖(S, J)→T(S, T)→JT→JT 是决定因素但非码)。
  • 分解步骤:
    • 提取 T→JTJ(T, J)
    • 保留剩余属性:ST(S, T)
  • 结果TJ∈BCNFST∈BCNF(所有决定因素均包含码)。

四、寻找码的实用技巧

(一)属性分类法
  1. L 类属性 :只出现在函数依赖左部(如 SnoOrderNo),必为主属性,包含在候选码中。
  2. R 类属性 :只出现在函数依赖右部(如 GradeSloc),必为非主属性,不包含在候选码中。
  3. N 类属性:未出现在任何函数依赖中,必包含在候选码中(全码场景)。
  4. LR 类属性 :同时出现在左、右部(如 SdeptT),需进一步判断。
(二)闭包计算法(简化版)
  1. 步骤:
    • 假设候选码为 X,从 L 类和 N 类属性开始,逐步添加 LR 类属性。
    • 计算 X 的闭包 X+,若 X+ = U,则 X 是候选码。
  2. 示例:
    • 关系模式 销售(商店编号, 商品编号, 库存数量, 部门编号, 负责人)
    • L 类:商店编号, 商品编号;R 类:库存数量, 部门编号, 负责人
    • 计算 (商店编号, 商品编号)+:通过函数依赖推导,可决定所有属性,故为候选码。

五、规范化实战:书店订单案例

题目:

考虑某书店的图书销售关系Book_Order,该关系涉及的属性包括:OrderNo(订单号)、ClientNo(客户号)、ClientName(客户名)、Address(客户地址)、BookNo(书号)、Title(书名)、Publisher(出版社)、Price(单价)、Quantity(订购数量)。

根据现实世界已知的事实,对于上述关系有如下语义:

•订单号、客户号和书号分别是订单、客户和图书的标识。

•每份订单只对应唯一的一个客户,但一个客户可以有多个订单。

•每个客户有唯一的客户名称和地址。

•每种图书有唯一的书名、出版社和单价。

•每份订单可以包含多种图书,每种图书可以在多份订单中订购。

•每份订单订购每一种图书,有唯一的订购数量。

(一)原始关系:Book_Order
  • 属性OrderNo, ClientNo, ClientName, Address, BookNo, Title, Publisher, Price, Quantity

  • 函数依赖
    OrderNo→ClientNo

    ClientNo→ClientName, Address

    BookNo→Title, Publisher, Price

    (OrderNo, BookNo)→Quantity

  • 主码:(orderNo,BookNo)

  • 问题 :非主属性 ClientName, Address, Title 等存在部分依赖和传递依赖。

(二)分解过程
  1. 第一步:拆分为 3 个关系(消除部分依赖)
    • OrderClient(OrderNo, ClientNo)(主码 OrderNo
    • Client(ClientNo, ClientName, Address)(主码 ClientNo
    • OrderBook(OrderNo, BookNo, Quantity)(主码 (OrderNo, BookNo)
    • Book(BookNo, Title, Publisher, Price)(主码 BookNo
  2. 验证:
    • 所有非主属性完全依赖于码,且无传递依赖,满足 3NF。
    • 若需进一步优化(如 BookPublisher 是否需要独立成表),可根据业务需求决定是否拆至 BCNF。

六、核心总结:范式选择与平衡

  • 优先目标

    一般业务系统分解至 3NF 即可,避免过度分解导致多表连接性能下降。

  • 判断口诀

    • 1NF:属性原子化,拒绝 "表中表"。
    • 2NF:码是组合键时,检查非主属性是否部分依赖。
    • 3NF:非主属性间不能 "间接决定"(如 A→B→C 需拆)。
    • BCNF:主属性也不能 "任性决定"(如 教师→课程 需拆)。
  • 终极目标

    消除冗余与异常,同时兼顾查询效率,通过索引优化关联查询(如为外码添加索引)

相关推荐
数据库小组3 小时前
2026 年,MySQL 到 SelectDB 同步为何更关注实时、可观测与可校验?
数据库·mysql·数据库管理工具·数据同步·ninedata·selectdb·迁移工具
华科易迅3 小时前
MybatisPlus增删改查操作
android·java·数据库
Kethy__4 小时前
计算机中级-数据库系统工程师-计算机体系结构与存储系统
大数据·数据库·数据库系统工程师·计算机中级
SHoM SSER4 小时前
MySQL 数据库连接池爆满问题排查与解决
android·数据库·mysql
熬夜的咕噜猫4 小时前
MySQL备份与恢复
数据库·oracle
jnrjian4 小时前
recover database using backup controlfile until cancel 假recover,真一致
数据库·oracle
lifewange5 小时前
java连接Mysql数据库
java·数据库·mysql
大妮哟5 小时前
postgresql数据库日志量异常原因排查
数据库·postgresql·oracle
还是做不到嘛\.6 小时前
Dvwa靶场-SQL Injection (Blind)-基于sqlmap
数据库·sql·web安全
不写八个6 小时前
PHP教程004:php链接mysql数据库
数据库·mysql·php