#数据完整性

数据完整性主要是指数据库中的数据始终正确、一致且有效,数据完整性涉及各个方面,例如确保数据满足业务的定义和准则要求:身份证号必须是唯一的、员工的薪酬范围和职称存在某种关系、员工的居住地必须是存在的等。

在实际应用中,保证数据的完整性非常重要,通常可以通过在数据库应用程序中执行业务规则或通过数据库触发器触发存储过程等多种方法来保证数据完整性。

YashanDB提供完整性约束功能,用于更便捷地从数据入库时就保证其完整性。

完整性约束是定义在列或对象上,用于限定数据库中的值必须遵守的规则(集)。数据库管理人员和业务程序开发人员明确声明数据完整性约束,以此来保证数据的完整性与业务正常运行。

使用完整性约束有以下优点:

  • 易用:可以使用SQL语句定义完整性约束,而无需任何额外的编程。SQL语句易于编写,同时也可以排除编程的错误。

  • 规则集中且统一:完整性约束定义在表上,存储在数据字典中。所有关于表数据的业务程序操作都必须遵守统一的完整性约束,对约束规则的变动无感知。

  • 灵活:加载大量数据时,可以临时停用完整性约束(待数据加载完成后再重新启用),减少/避免不必要的性能开销。

# 完整性约束的类型

YashanDB数据库支持在列级和表级应用约束,在建表时,可以直接在关于列的描述后指定约束,也可以在整个建表语句末尾添加约束。

完整性约束可以分为如下类型:

约束类型 描述
非空约束(NOT NULL) 指定列是否可以为NULL,只能在建表语句中关于列的描述后指定非空约束。
唯一约束(Unique key) 在相同的列、或多个列的组合中,是否允许不同的行拥有重复的值(允许值为NULL)。
主键约束(Primary key) 同时满足非空约束和唯一约束。
外键约束(Foreign key) 指定一个列作为外键,在外键和主键或唯一键之间建立关系,也称为引用键。外键的值必须在主键或唯一键内存在。
检查性约束(Check) 要求对应列满足指定的条件。

# 非空约束

默认情况下,一个表中的所有列都允许空值,使用NOT NULL约束可以指定列不允许为空值。

NOT NULL约束主要用于不能缺少值的列,例如员工信息表中的姓名列。如果姓名列指定了NOT NULL约束,在插入没有姓名的员工信息时会提示错误:

INSERT INTO employees(employee_id) VALUES(1);

YAS-04006 cannot insert NULL value to column employee_name

如果表中已包含数据,不能再添加一个具有NOT NULL约束的列,除非对应列还带有DEFAULT值定义。

# 唯一约束

唯一约束规定了在指定列或列集中的每个值都是唯一的。即在一个表中,不允许多个行在有唯一约束的列(唯一键)或列集(复合唯一键)上具有重复值。

唯一键约束适合于任何不允许重复值的列,但唯一约束通常只要求值唯一,并不一定具有实际意义。

唯一约束列都为空值也始终满足唯一键约束。

# 主键约束

在一个主键约束中的列或列集,其键值能唯一地标识一行。每个表只能有一个主键,起确定行的作用,并确保不存在任何重复的行。

主键可以是表中有意义的属性列(例如身份证号),也可以是一个无意义的列(例如人为给表设置一个序列ID列)。

YashanDB主键约束可以保证:

  • 任何两行在指定的主键列上都不具有重复值。

  • 主键列不允许为空值。

例如员工信息表中的编号标识,每个员工必须拥有唯一的ID(工号),每名员工信息只能由员工表中的有且仅有的一行来记录:

INSERT INTO employees(employee_id) VALUES (1);

YAS-02030 unique constraint violated

INSERT INTO employees(name) VALUES('小明');

YAS-04006 cannot insert NULL value to column employee_id

YashanDB使用索引实现主键约束。通常,在某列上创建主键约束会隐含创建一个唯一索引和一个非空约束,但如果创建主键约束时已有一个现成的索引可用,则主键约束会直接复用该索引,不会隐式创建额外的新索引。

# 外键约束

当两个表拥有一个或多个公共列时,YashanDB可以通过创建一个外键约束来建立这两个表之间的关联关系。

通常,外键约束要求约束列或列集中的每个值都必须与另一个表的指定列中的值相匹配,例如教师所带的班级必须是班级信息表中存在(教师信息表到班级信息表的外键约束)。

术语 定义
外键/复合外键 约束定义中包含的列称为外键,一个外键由多个列组成时称为复合外键。
外键会引用另一个表的主键或唯一键,复合外键则需引用相同数量和数据类型列的复合主键或复合唯一键。
外键的值,要么匹配被引用主键或唯一键的值,要么为空。如果复合外键中存在空值列,该键的非空列不再强制要求匹配父项中的对应列。
被引用键 被外键所引用的表中的唯一键或主键。
依赖表 又称“子表”,外键约束所在的表。
被引用表 又称“父表”,被子表的外键引用的表。该表中的被引用值决定了在子表中特定的插入或更新是否可被允许。

# 父表的修改与外键的关系

删除或更新父表数据可能会破坏外键约束。在父键被修改时,为了满足外键约束,参照完整性约束可以指定在子表中的相关行上,执行以下某种操作之一:

  • NO ACTION

    在正常的情况下,如果修改结果会违反外键约束,用户不能做此修改。

  • DELETE CASCADE

    级联删除(DELETE CASCADE)是指当父表被删除时,对应被引用键值在子表中的所有行也同时被删除。

  • DELETE SET NULL

    删除置空(DELETE SET NULL)是指当父表被删除时,对应被引用键值在子表中的所有行的外键被设置成NULL。

  • UPDATE CASCADE

    级联更新(UPDATE CASCADE)是指当父表更新时,对应被引用键值在子表中的所有行也同时按照新值更新。

  • UPDATE SET NULL

    更新置空(UPDATE SET NULL)是指当父表更新时,对应被引用键值在子表中的所有行的外键被设置成NULL。

# 索引和外键

YashanDB建议在外键列上创建索引,优势如下:

  • 可以防止修改父表触发子表的排他表锁。

  • 可以防止修改父表触发子表的全表扫描。

# 检查性约束

检查约束具备强制执行具体的完整性规则的能力,对指定列或列集创建检查性约束,可以保证表中的数据一定满足指定的条件。如果DML语句违反了检查性约束的条件,执行会报错。

在age列中设置不能大于60的检查约束,插入示例如下:

INSERT INTO employees(age) VALUES(61);

YAS-02254 check constraint (MAX_EMP_AGE) violated

允许对同一列同时指定多个检查性约束,但应保证所有检查性约束的合理性,否则可能会导致表不可用。例如:某一列同时被指定“值必须大于5000”和“值必须小于4999”,两个检查性约束互斥,设置不合理。

# 完整性约束的表现

YashanDB可以指定将某个约束是否启用(对象是新数据)和是否验证(对象是现有数据),从而确定具体的约束表现。

如果指定启用(ENABLE),插入/更新数据时会检查新数据是否符合约束,违反约束的数据无法插入/更新至表中。

如果指定验证(VALIDATE),会验证现有数据是否符合该约束。如果现有数据存在违反约束,则指定验证(VALIDATE)失败并报错。唯一约束以及主键约束由唯一索引保证,即使指定为不验证(NOVALIDATE)依然会验证,如果现有数据违反约束依然会返回报错。

检查与验证双重作用下,具体的约束表现如下:

是否启用 是否验证 约束表现
ENABLE VALIDATE 表中现有数据和未来的新数据都必须满足约束。
ENABLE NOVALIDATE 表中现有数据可以不满足约束,但未来的新数据必须满足约束。
DISABLE VALIDATE 表中现有数据必须满足约束,但是当前约束失效(约束上的索引也一并删除),未来的新数据无需满足约束。
DISABLE NOVALIDATE 表中现有数据可以不满足约束,同时当前约束失效,未来的新数据无需满足约束。