数据库是现代信息系统的心脏,数据的准确性和一致性对于业务流程至关重要。数据库完整性是确保数据质量的基石,它涵盖了数据的正确性、相容性和一致性,是数据安全与业务连续性的保障。
数据库完整性是指数据的精确性、可靠性和逻辑一致性,它防止数据库中出现不符合语义的数据。其目的是保护数据免受意外或恶意破坏,确保数据在逻辑上的一致性、正确性和有效性。
数据库的完整性(integrity)是指数据的正确性(correctness)和相容性(compat-ability):
数据库的完整性是为了防止数据库中存在不符合语义的数据,也就是防止数据库中存在不正确的数据。因此,完整性检查和控制的防范对象是不合语义的、不正确的数据,防止它们进入数据库。
而数据库的安全性是保护数据库防止恶意破坏和非法存取。因此,安全性控制的防范对象是非法用户和非法操作,防止他们对数据库数据的非法存取。
为维护完整性 DBMS
必须要实现如下功能:
提供完整性检查的方法:检查数据是否满足完整性约束条件的机制。一般在 INSERT
、UPDATE
、DELETE
语句执行后开始检查,也可以在事务提交时检查。检查这些操作执行后数据库中的数据是否违背了完整性约束条件。
进行违约处理:数据库管理系统若发现用户的操作违背了完整性约束条件将采取一定的动作,如拒绝实体完整性(NOACTION)执行该操作或级联(CASCADE)执行其他操作,进行违约处理以保证数据的完整性。
关系模型的实体完整性在 CREATE TABLE
中用 PRIMARY KEY
定义。
对单属性构成的码有两种说明方法
PRIMARY KEY
关键字。CREATE TABLE table_name(
Sno CHAR(10) PRIMARY KEY,
-- ...
)
PRIMARY KEY
关键字并括上需要作为主键的列名。CREATE TABLE table_name(
Sno CHAR(10),
-- ...
PRIMARY KEY(Sno)
)
CREATE TABLE table_name(
Sno CHAR(10),
Cno CHAR (10),
-- ...
PRIMARY KEY(Sno,Cno)
)
用 PRIMARY KEY
短语定义了关系的主码后,每当用户程序对基本表插入一条记录或对主码列进行更新操作时,关系数据库管理系统将按照关系数据库的实体完整性规则自动进行检查:
检查主码值是否唯一,如果不唯一则拒绝插入或修改。
检查主码的各个属性是否为空,只要有一个为空就拒绝插入或修改。
检查记录中主码值是否唯一的一种方法是进行全表扫描,依次判断表中每一条记录的主码值与将插入记录的主码值(或者修改的新主码值)是否相同。
全表扫描是十分耗时的。为了避免对基本表进行全表扫描,关系数据库管理系统一般都在主码上自动建立一个索引,如B+树索引,通过索引查找基本表中是否已经存在新的主码值将大大提高效率。
关系模型的参照完整性在 CREATE TABLE
中用 FOREIGN KEY
短语定义哪些列为外码,用 REFERENCES
短语指明这些外码参照哪些表的主码。
CREATE TABLE SC(
Sno CHAR(9)NOT NULL,
Cno CHAR(10) NOT NULL,
-- ...
-- 表级定义实体完整性
PRIMARY KEY(Sno,Cno),
-- 表级定义参照完整性
FOREIGN KEY(Sno) REFERENCES Student(Sno),
FOREIGN KEY(Cno) REFERENCES Course(Cno)
)
参照完整性将两个表中的相应元组联系起来了。因此,对被参照表和参照表进行增删、改操作时有可能破坏参照完整性,必须进行检查以保证这两个表的相容性。
可能破坏参照完整性的情况:
SC
表中增加一个元组,该元组的 Sno
属性值在表 Student
中找不到一个 Sno
属性值与之相等的元组。
修改 SC
表中的一个元组,修改后该元组的 Sno
属性值在表 Student
中找不到一个元组,其 Sno
属性值与之相等。
从 Student
表中删除一个元组,造成 SC
表中某些元组的 Sno
属性值在表 Student
中找不到一个元组,其 Sno
属性值与之相等。
修改 Student
表中一个元组的 Sno
属性,造成 SC
表中某些元组的 Sno
属性值在表 Student
中找不到一个元组,其 Sno
属性值与之相等
当上述的不一致发生时,系统可以采用以下策略加以处理:
拒绝 NOACTION
执行:不允许该操作执行。该策略一般设置为默认策略。
级联 CASCADE
操作:当删除或修改被参照表的一个元组导致与参照表的不一致时,删除或修改参照表中的所有导致不一致的元组。
设置为空值 NULL
:当删除或修改被参照表的一个元组时造成了不一致,则将参照表中的所有造成不一致的元组的对应属性设置为空值。(设置为主码的外码不能设置为空值,违反实体完整性)
当对参照表和被参照表的操作违反了参照完整性时,系统选用默认策略,即拒绝执行。
如果想让系统采用其他策略则必须在创建参照表时显式地加以说明:
CREATE TABLE SC
(
Sno CHAR(9)
Sno CHAR(4)
PRIMARY KEY(Sno,Cno),
FOREIGN KEY(Sno) REFERENCES Student(Sno)
-- 删除 Student 表的元组时,级联删除本表相应元组
ON DELETE CASCADE
-- 更新 Student 表中的 Sno 时,级联更新本表相应元组
ON UPDATE CASCADE,
FOREIGN KEY(Cno) REFERENCES Course(Cno)
-- 删除 Course 表的元组造成不一致时,拒绝执行
ON DELETE NO ACTION
-- 更新 Course 表中的 Cno 时,级联更新本表相应元组
ON UPDATE CASCADE
);
用户自定义完整性针对某一具体应用所涉及的数据必须满足的语义要求,目前的关系数据库管理系统都提供了定义和检验这类完整性的机制,使用了和实体完整性、参照完整性相同的技术和方法来处理它们,而不必由应用程序承担这一功能。
属性上的条件约束:在 CREATE TABLE
中定义属性的同时,可以根据应用要求定义属性上的约束条件,即属性值限制,包括:
NOTNULL
:列值非空UNIQUE
:列值唯一CHECK(condition)
短语:检查列值是否满足一个条件表达式CREATE TABLE table_name(
Sno CHAR(10) NOT NULL PRIMARY KEY;
Cno CHAR(10) UNIQUE NOT NULL,
Grade INT CHECK(Grade>=0 AND Grade<=100)
-- ...
)
CREATE TABLE
中可以用 CHECK短语
定义元组上的约束条件,即元组级的限制。同属性值限制相比,元组级的限制可以设置不同属性之间的取值的相互约束条件。CREATE TABLE table_name(
Gender CHAR(4),
Sname VARCHAR(10),
-- ...
CHECK(Gender='男' OR Sname NOT LIKE 'Ms.%')
)
SQL 还在 CREATE TABLE 语句中提供了完整性约束命名子句CONSTRAINT
,用来对完整性约束条件命名从而可以灵活地增加、删除一个完整性约束条件。
列级约束:直接在列定义中使用 PRIMARY KEY
关键字。
表级约束:在列定义之后,使用 PRIMARY KEY
关键字并括上需要作为主键的列名。
CONSTRAINT 'constraint_name' constraint_condition
constraint_name
:完整性约束条件名
constraint_condition
:完整性约束条件(UNIQUE
、CHECK
等)
UNIQUE
约束:保证列或列组合的值是唯一的,但允许NULL值的存在。CHECK
约束:定义列值必须满足的条件,可以是算术或逻辑表达式。CREATE TABLE table_name(
Sno CHAR(10)
CONSTRAINT con1 NOT NULL, PRIMARY KEY,
Sage INT
CONSTRAINT con2 CHECK(Sage<=18 AND Sage>=0)
)
借助 ALTER TABLE
语句可以动态地添加或删除表中的完整性限制
删除限制:
ALTER TABLE table_name
DROP CONSTRAINT con1
添加限制:
ALTER TABLE table_name
ADD CONSTRAINT con3 UNIQUE(Sage)
若需要修改限制,则可以删除指定限制后重新添加限制。
在使用复合主键(由多个列组成)时,确保所有组合的值在所有行中都是唯一的。
当使用 CHECK
约束时,确保条件逻辑的正确性,避免无效或过于复杂的约束条件。
在修改约束之前,考虑其对现有数据的影响,确保不会违反新约束。
在 SQL 中可以使用数据定义语言中的 CREATE ASSERTION
语句,通过声明性断言(declarative assertions)来指定更具一般性的约束(如涉及多表、聚集操作等)。可以定义涉及多个表或聚集操作的比较复杂的完整性约束。
断言创建以后,任何对断言中所涉及关系的操作都会触发关系数据库管理系统对断言的检查,任何使断言为 FALSE 的操作都会被拒绝执行。
MySQL 不支持 ASSERTION 关键字
在SQL中,使用 CREATE ASSERTION
语句来创建断言
CREATE ASSERTION <断言名><CHECK 子句>
每个断言都被赋予一个名字,
中的约束条件与 WHERE 子句的条件表达式类似。
DROP ASSERTION<断言名>
如果断言很复杂,则系统在检测和维护断言上的开销较高,这是在使用断言时应该注意的。