【数据库】数据库的完整性
数据库完整性包含正确性、有效性和相容性,主要是为了防范不合语义的、不正确的数据。
这种完整性约束条件分成几类。按照约束条件对象关系,区分为关系-元组间的约束、元组-元组间的约束和列-字段的约束;按照约束的状态,可以分成静态条件(某个确定状态时数据的约束条件)和动态(从一个状态到另一个状态的新旧值约束条件)条件。
DBMS需要完成:
- 定义完整性约束条件机制
- 提供完整性检查的方法
- 违约处理
通过这样的机制,就可以保证数据库完整性。
实体完整性
对于关系模型,使用PRIMARY KEY
来定义实体完整性。实体完整性要求主码不能取空。对于单属性构成的码,可以定义为列级,也可以定义为表级;多个属性构成的码只能有一种说明方法。
CREATE TABLE S {Sno CHAR(9) PRIMARY KEY, /*列级定义主码*/...
}
CREATE TABLE S {Sno CHAR(9), ...PRIMARY KEY(Sno) /*表级定义主码*/
}
对于插入和更新操作,要检查主码是否唯一,并且每个属性不为空。
参照完整性
CREATE TABLE SC (Sno CHAR(9) NOT NULL,Cno CHAR(4) NOT NULL,Grade SMALLINT,PRIMARY KEY (Sno, Cno), /*在表级定义实体完整性*/FOREIGN KEY (Sno) REFERENCES Student(Sno),/*在表级定义参照完整性*/FOREIGN KEY (Cno) REFERENCES Course(Cno)/*在表级定义参照完整性*/
);
对于参照完整性的违约,情况如下:
被参照表 | 参照表 | 违约处理 |
---|---|---|
可能破坏参照完整性 | 插入元组 | 拒绝 |
可能破坏参照完整性 | 修改外码值 | 拒绝 |
删除元组 | 可能破坏参照完整性 | 拒绝/级联删除/设置为空 |
修改主码值 | 可能破坏参照完整性 | 拒绝/级联修改/设置为空 |
如果设置为空,还需要设置外码列是否为空。对于其设置可以使用这样的方法:
FOREIGN KEY (Sno) REFERENCES Student(Sno) ON DELETE CASCADE /*级联删除SC表中相应的元组*/ON UPDATE SET NULL, /*级联更新SC表中相应的元组*/
FOREIGN KEY (Cno) REFERENCES Course(Cno) ON DELETE NO ACTION /*当删除course 表中的元组造成了与SC表不一致时拒绝删除*/ON UPDATE CASCADE /*当更新course表中的cno时,级联更新SC表中相应的元组*/
用户自定义完整性
一、属性级别的约束条件
对于属性级别,分成三种:
- NOT NULL
- UNIQUE
- CHECK
下面进行举例。
NOT NULL:
CREATE TABLE SC (Sno CHAR(9) NOT NULL,Cno CHAR(4) NOT NULL,Grade SMALLINT NOT NULL
)
UNIQUE:
CREATE TABLE DEPT(Deptno NUMERIC(2),Dname CHAR(9) UNIQUE
)
CHECK:
CREATE TABLE Student (Sno CHAR(9) PRIMARY KEY,Ssex CHAR(2) CHECK (SSex IN ('男','女'))
)
如果约束不满足则会拒绝执行。
二、元组级别的约束条件
使用CHECK。
CREATE TABLE Student (Sno CHAR(9) PRIMARY KEY,Sname CHAR(8) NOT NULL,SSex CHAR(2) CHECK (SSex IN ('男','女')),CHECK(SSex = '女' OR Sname NOT LIKE 'Ms.%')/*男性不能以Ms.打头*/
)
如果约束不满足则会拒绝执行。
三、约束子句
使用CONSTRAINT进行约束。
CONSTRAINT <约束名> [PRIMARY KEY 短语|FOREIGN KEY 短语|CHECK 短语
]
比如建立Student表,要求学号在90000-99999之间,姓名不能取空,年龄小于30,性别为男或女。
CREATE TABLE Student(Sno NUMERIC(6)CONSTRAINT C1 CHECK(Sno BETWEEN 90000 AND 99999),Sname CHAR(20)CONSTRAINt C2 NOT NULL,Sage NUMERIC(3)CONSTRAINT C3 CHECK(Sage < 30),Ssex CHAR(2)CONSTRAINT C4 CHECK(SSex IN('男','女')),CONSTRAINT StudentKey PRIMARY KEY(Sno)
)
如果需要修改完整性限制,使用ALTER TABLE语句
比如把之前的SSex进行修改:
ALTER TABLE Student
DROP CONSTRAINT C4;
ALTER TABLE Student
CONSTRAINT C4 CHECK(SSex IN('男','女','其它'))
四、域的完整性约束
可以创建一个域进行取值贤治。
CREATE DOMAIN GenderDomain VARCHAR(2)
CHECK(VALUE IN('男','女','其它'))
这个时候,对SSex说明可以直接使用SSex GenderDomain
也可以对其域进行一定的修改:
/*建立*/
CREATE DOMAIN GenderDomain CHAR(2)
CONSTRAINT GD CHECK(VALUE IN('男','女'));
/*删除*/
ALTER DOMAIN GenderDomain
DROP CONSTRAINT GD;
/*增加*/
ALTER DOMAIN GenderDomain
ADD CONSTRAINT GDD CHECK(VALUE IN('1', '0'))
触发器
触发器是用户顶i有的一类事件驱动的特殊过程,由服务器自动激活。对触发器的定义使用下面的形式:
CREATE TRIGGER <触发器名>{BEFORE|AFTER} <触发事件(INSERT|DELETE|UPDATE)> ON <表名>FOR EACH {ROW | STATEMENT}[WHEN <触发条件>]<触发动作体>
事实上,可以用五元组 ( D , O , A , C , P ) (D,O,A,C,P) (D,O,A,C,P)来表示一个完整性规则,其中
- D:数据
- O:触发完整性约束的操作
- A:约束条件
- C:选择数据对象值的谓词
- P:违反完整性约束时出发的处理
这里要注意的是,触发器类型可以分成行级(ROW)或者语句级(STATEMENT)。比如下面的语句
UPDATE Teacher SET Deptno = 5
那么语句级触发级只执行一次,行级有多少行就执行多少次。
考虑下面的例子:一个BEFORE行级触发器,让Teacher定义规则:教授工资不得低于4000,低于就自动改成4000.这个时候,可以使用下面的语句:
CREATE TRIGGER Insert_Or_Update_SalBEFORE INSERT OR UPDATE ON TeacherFOR EACH ROWAS BEGINIF (new.Jpb = '教授') AND (new.Sal < 4000) THENnew.sal := 4000;END IF;END;
再看一个例子。我们试图实现在教师表的工资变化的时候自动在工资变化表Sal_log增加相应记录。为此,先建立Sal_log表
CREATE TABLE Sal_log(Eno NUMERIC(4) references teacher(eno),Sal NUMERIC(7,2),Username CHAR(10),Date TIMESTAMP
);
然后定义触发器
CREATE TRIGGER Insert_SalAFTER INSERT ON TeacherFOR EACH ROWAS BEGININSERT INTO Sal_log VALUES(new.Eno, new.Sal, CURRENT_USER, CURRENT_TIMESTAMP;);END;
CREATE TRIGGER Update_SalAFTER UPDATE ON TeacherFOR EACH ROWAS BEGINIF (new.Sal <> old.sal) THEN INSERT INTO Sal_log VALUES(new.Eno, new.Sal, CURRENT_USER, CURRENT_TIMESTAMP;);END IF;END;
触发器是自动激活的,一般遵循BEFORE触发器-SQL语句-AFTER触发器的执行顺序。
删除触发器使用DROP语句:
DROP TRIGGER <触发器名> ON <表名>