完整性约束的作用在于保证授权用户对数据库所做的修改不会破坏数据的一致性。或者说,防止的是对数据的意外破坏。
单个关系的约束
一般而言,约束是在创建表的时候生成,例如我们在之前的《SQL——SQL数据定义》
有这样的例子
CREATE TABLE department(
dept_name VARCHAR(20),
building VARCHAR(15),
budget NUMERIC(12,2),
PRIMARY KEY (dept_name)
);
完整性约束有三种:
- not null
- unique
- check(<P>)
not null 约束
not null 约束是使得被约束的属性的值不能 (禁止)为空值。例如,我们要将dept_name设置为非空:
dept_name VARCHAR(20) NOT NULL
不过在这里dept_name也可以不加not null,因为这里not null 是主码,是默认不能为空。
unique 约束
通式:
unique<A1,A2,...An>
unique声明<A1,A2,…An>是一个候选码,即在关系中没有两个元祖在所有这些列出的属性上取值相同,但可以同时为空。因为空值不等于任何值。
示例:
unique(building)
#或者
unique(building,budget)
#或者(该方法只适用于约束单个属性)
building unique
check子句
check子句自定一个谓词P,关系中的每一个元组都必须满足谓词P,例如:
#保证department中的budget>0,那么可以这样写:
CREATE TABLE department(
dept_name VARCHAR(20),
building VARCHAR(15),
budget NUMERIC(12,2),
PRIMARY KEY (dept_name),
CHECK(budget>0)
);
#保证section表的semester是集合中的字符串
check(semester in{'fall','spring','winter','summer'})
参照完整性
我们常常希望保证在一个关系中给定属性集上的取值也在另一关系的特定属性集的取值中出现,这种情况称为参照完整性。
我们令关系r1和关系r2的属性集为R1和R2,主码分别为K1和K2。如果要求对r2中任意元组t2,均存在r1中元组t1使得t.K1=t2.α,我们称R2的子集α为参照关系r1中K1的外码。
这种要求称为参照完整性约束或者子集依赖。
SQL中 外码参照的是被参照表中的主码属性,可用reference指定,指定的属性列表必须被声明为被参照关系的候选码,即被primary或者unique约束的属性。
例如
#dept_name是外码,department是被参照关系,department中dept_name为主码
dept_name varchar(20)reference department
#或者写成下面的形式
dept_name varchar(20),
foreign key(dept_name) reference department(dept_name)
复杂check条件和断言
复杂check条件
SQL标准定义,check子句中的谓词可以是包含子查询的任意谓词。如果一个数据库实现支持在check子句中出现子查询(如果不支持就不能这样使用),我们就可以在关系section上声明如下所示的参照完整性约束:
check (time_slot_id in(select time_slot_id from time_slot))
像这样的复杂check条件在我们希望确保数据完整性的时候是很有用的,但是其检测开销可能会很大。例如,check子句中的谓词不仅需要在section关系发生更新时计算,而且也可能在time_slot关系发生更新时检测,因为time_slot在子查询中被引用了。
断言
一个断言(assertion)就是谓词,它表达了我们希望数据库总能满足的一个条件。域约束和参照完整性约束是断言的特殊形式。前面几种约束(像not null等),有些约束不能仅用这几种约束来表达。例如:
对于student关系中的每个元组,它在属性tol_cred上的取值必须等于该学生所成功修完课程的学分总和。
每位教师不能在同一学期的同一时间段在两个不同的教室授课。
这是需用断言来实现,断言为如下的形式
create assertion <assertion-name> check <predicate>;
SQL不提供“for all X,P(X)”,即不提供对于任意x,使得P(x)的结构,那么我们只能通过等价的“not exists X such that not P(x)”,即不存在X,使得非P(x)。对于第一个例子可以这样实现:
CREATE ASSERTION credit_earned_constraint CHECK (
NOT exists(
SELECT id
FROM student
WHERE tol_cred<>(
SELECT sum(credits)
FROM takes NATURAL JOIN course
WHERE student.id = takes.id
AND grade IS NOT NULL AND grade>=60
)
)
);
当创建断言时,系统要检测其有效性。如果断言有效,则今后只有不破坏断言的数据库修改才被允许。如果断言较复杂,则检测会带来相当大的开销,应该注意。