數據庫的完整性(integrity)是指數據的【正確性(correctness)】和【相容性(compat-ability)】
案例
學生的學號必須唯一 性別只能是男或女 本科學生年齡的取值范圍為14~50的整數 學生所選的課程必須是學校開設的課程 學生所在的院系必須是學校已經成立的院系。
為了保證DB的完整性,DBMS必須實現如下功能:
定義實體完整性(entity integrity)
- -> 列級約束條件
- -> 表級約束條件
對單屬性構成的碼
[案例:將Student表的Sno屬性定義為碼]
CREATE TABLE Student
(
Sno CHAR(9) PRIMARY KEY, /*列級約束條件*/
Sname CHAR(20) NOT NULL,
Sage SMALLINT
//, PRIMARY KEY(Sno) /*表級約束條件*/
);
對多屬性構成的碼
[案例:將SC表中的Sno、Cno屬性組定義為碼]
CREATE TABLE SC
(
Sno CHAR(9) NOT NULL,
Cno CHAR(4) NOT NULL,
Grade SMALLINT,
PRIMARY KEY(Sno, Cno) /*只能在表級定義主碼*/
);
實體完整性檢查和違約處理(entity integrity check and default handling)
實體完整性規則的檢查:
(1)主碼值是否唯一,若不唯一則拒絕插入或修改.
(2)主碼值是否為空,若為空格則決絕插入或修改.
實現完整性規則檢查方法兩種
第一種是【全表掃描】: 依次判斷表中每一條記錄的主碼值與將插入記錄的主碼值判斷是否相同若相同決絕插入。
第二種是【構建索引】: 由於全表掃描過於耗時所以要避免全表掃描通過在主碼上自動建立一個索引。
通過B+樹索引查找基本表中是否已經存在新的主碼值將大大提高效率
參照完整性(referential integrity)
- FOREIGN KEY定義哪些列為外碼
- REFERENCES短語指明這些外碼參加哪些表的主碼。
[案例:關系SC中一個元組即一個學生學修某門課程的成績,(Sno, Cno)是主碼Sno,Cno分別參照引用Student表和Course表的主碼]
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), /*表級定義參照完整性*/
);
用戶定義完整性(User-defined Integrity)
用戶定義完整性【針對某一具體應用】的數據必須滿足的語義要求
屬性上的約束條件
在CREATE TABLE中定義屬性的同時確定屬性的約束條件。
- 列值非空(NOT NULL)
- 列值唯一(UNIQUE)
- 檢查列值是否滿足一個條件表達式(CHECK)
【案例:定義一個員工表】
CREATE TABLE emp
(
emp_no INT,
emp_nickname UNIQUE NOT NULL, /*UNIQUE + NOT NULL = PRIMARY KEY 即列值唯一不為空*
gender CHAR(2) CHECK(gender IN ('男', '女')),
job_level SMALLINT CHECK(credit > 0 AND credit < 10),
PRIMARY KEY(emp_no) /*表級實體完整性*/
);
當向表中插入元組或修改屬性的值時,DBMS將檢查屬性上的約束是否被滿足否則拒絕執行。
元組上的約束條件
在CREATE TABLE中可以用CHECK短語定義元組上的約束條件(constraint condition)即元組級別的限制。
【案例:當學生的性別是男時,其名字不能以Ms.打頭】
CREATE TABLE Student
(
Sno CHAR(9),
Sname CHAR(8) NOT NULL,
Ssex CHAR(2),
Sage SMALLINT,
Sdept CHAR(20),
PRIMARY KEY(Sno),
CHECK(Ssex='女' OR Sname NOT LIKE 'Ms.%') /*定義元組中Sname和Ssex兩個屬性值之間的約束條件*/
);
當性別為男性的時候則要求名字不能以Ms.大頭
若Ssex='男',條件若想為真則 Sname NOT LIKE 'Ms.%' 必須為真
當插入元組時不滿足元組級別約束條件則拒絕插入。
完整性約束名子句
給約束定義別名利用CONSTRAINT
格式:
CONSTRAINT <完整性約束條件名><完整性約束條件>
<完整性約束條件>包括UNIQUE、NOT NULL、PRIMARY KEY、FOREIGN KEY、CHECK短語等。
[案例 建立學生登記表Student,要求學號在90000~99999之間,姓名不能取空值,年齡小於30,性別只能是“男”或“女”]
CREATE TABLE Student
(
Sno NUMERIC(6)
CONSTRAINT c_1 CHECK(Sno BETWEEN 90000 AND 99999),
Sname CHAR(20)
CONSTRAINT c_2 NOT NULL,
Sage NUMERIC(3)
CONSTRAINT c_3 CHECK(Sage < 30),
Ssex CHAR(3)
CONSTRAINT c_4 CHECK(Ssex IN('男', '女')),
CONSTRAINT StudentKey PRIMARY KEY(Sno)
);
/*在Student表上建立5個約束條件,包括主碼約束、c_1、c_2、c_3、c_4*/
[建立教師表Teacher,要求每個教師的應發工資不低於3000元,應發工資是工資列Sal與扣除項Deduct之和]
CREATE TABLE Teacher
(
Eno NUMERIC(4) PRIMARY KEY, /*在列級定義主碼*/
Ename CHAR(10),
Job CHAR(8),
Sal NUMERIC(7,2),
Deduct NUMERIC(7,2),
Deptno NUMERIC(2),
CONSTRAINT TeacherKey FOREIGN KEY(Deptno) REFERENCE DEPT(Deptno),
CONSTRAINT c_1 CHECK(Sal + Deduct >= 3000)
);
修改表中的完整性限制
使用ALTER TABLE語句修改表中的完整性限制
[案例:去掉Student表中對性別的限制]
ALTER TABLE Student
DROP CONSTRAINT c_4;
[案例:修改表Student中的約束條件,要求學號改為900000~999999]之間,年齡由小於30改為小於40
可以先刪除原有的約束條件在添加新的約束條件
ALTER TABLE Student
DROP CONSTRAINT c_1;
ALTER TABLE Student
CONSTRAINT c_1 CHECK(Sno BETWEEN 900000 AND 999999);
ALTER TABLE Student
DROP CONSTRAINT c_3;
ALTER TABLE Student
ADD CONSTRAINT c_3 CHECK(Sage < 40);
域中的完整性限制
域:一組具有相同數據類型的值的集合
[建立一個性別域並聲明性別與的訪問]
第一種
CREATE DOMAIN GenderDoamin CHAR(2)
CHECK (VALUE IN('男', '女'));
[建立一個性別域GenderDomain並為其定義別名]
CREATE DOMAIN GenderDomain CHAR(2)
CONSTRAINT GD CHECK(VALUE IN ('男','女'));
[刪除域GenderDomain的限制條件GD]
ALTER DOMAIN GenderDomain
DROP CONSTRAINT GD;
[在域GenderDomain上增加限制條件GDD]
ALTER DOMAIN GenderDomain
ADD CONSTRAINT GDD CHECK(VALUE IN('1','0'));
觸發器(Trigger)
觸發器:用戶定義在關系表上的一類由事件驅動的特殊過程.
一旦定義觸發器將保存在DBServer中,用戶在對DB中的數據進行DML操作的時候將自動激活相應的觸發器.
觸發器類似於約束但比約束更為靈活可實施更為復雜的檢查和操作,具有更精細和強大的數據控制能力。
定義觸發器
觸發器又稱為【事件-條件-動作(event-condition-action)規則】
格式:
CREATE TRIGGER <觸發器名稱> /*每當觸發事件發生的時候,該觸發器將被激活*
{BEFORE|AFTER} <觸發事件> ON <表名> /*指明觸發器激活的時間是在執行觸發事件前或后*/
REFERENCING NEW|OLD ROW AS<變量> /*REFERENCING指出引用的變量*/
FOR EACH{ROW|STATEMENT} /*定義觸發器的類型,致命動作體執行的頻率*/
[WHERE<觸發條件>]<觸發動作體> /*僅當觸發條件為真的時候才會執行觸發動作體*/
注意項
- 只有表的擁有者才能在所擁有的表上設置觸發器
- 同一個模式下,觸發器名必須唯一
- 觸發器只能定義在基本表上,不能定義在視圖上
當基本表的數據發生變化的時候,將激活定義在該表上相應觸發事件的觸發器
- 觸發事件:【INERT、UPDATE、DELETE】或幾個動作的組合
- AFTER/BEFORE是觸發的時機
AFTER表明在觸發事件的操作執行之后激活觸發器;BEFORE表明在觸發事件的操作執行之前激活觸發器。
6.觸發器的類型: 行級觸發器(FOR EACH ROW)和語句級觸發器(FOR EACH STATEMENT)
行級觸發器對每一次的數據變動都要觸發相應的事件,語句級觸發器要到整個數據變動完畢之后才觸發相應的事件
eg: UPDATE TEACHER SET Deptno=5;
若TEACHER由1000行,對語句級觸發器要在UPDATE語句后觸發動作體執行一次,當對於行級觸發器,觸發動作體將執行1000次。
7.觸發條件: 當觸發器被激活時,只有當觸發條件為真時觸發動作體才會被執行,否則觸發動作體不執行。如果省略WHEN觸發條件,則觸發動作體在觸發器激活后立即執行。
【案例: 對表SC的Grade屬性修改,若分數增加10%,則將此次操作記錄到另一個表SC_U(Sno, Cno, Oldgrade, Newgrade)中,其中Oldgrade時修改前的分數,Newgrade是修改后的分數】
CREATE TRIGGER SC_T /*SC_T是觸發器的名字*/
AFTER UPDATE OF Grade ON SC /*UPDATE OF Grade ON SC是觸發事件即對SC表中Grade字段更新*/
/*AFTER是觸發的時機,表示對SC的Grade屬性修改完后再觸發下面的規則*/
REFERENCING
OLDROW AS OldTuple,
NEWROW AS NewTuple
FOR EACH ROW /*行級觸發器, 即每執行一次Grade的更新,下面的規則就執行一次*/
WHEN(NewTuple.Grade >= 1.1 * OldTuple.Grade) /*觸發條件,只有該條件為真時才執行*/
INSERT INTO SC_U(Sno, Cno, OldGrade, NewGrade)
VALUES(OldTuple.Sno, OldTuple.Grade, NewTuple.Grade)
REFERENCING指出引用的變量,如果觸發事件是UPDATE操作並由FOR EACH ROW子句則引用的變量有【OLDROW】和【NEWROW】,分別表示修改之前的元組和修改之后的元組
若沒有FOR EACH ROW子句,則可以引用的變量有OLDTABLE和NEWTABLE,OLDTABLE表示表中原來的內容,NEWTABLE表示表中變化后的部分。
【案例:將對表Student的插入操作所增加的學生個數記錄到表student-InsertLog】中
CREATE TRIGGER Student_Count
AFTER INSERT IN Student /*觸發器激活的事件是在對Student表執行更新的INSERT之后*/
REFERENCING
NEW TABLE AS DELTA
FOR EACH STATEMENT /*語句級觸發器,即執行完INSERT語句后下面的觸發動作體才執行一次*/
INSERT INTO StudentInsertLog(Numbers)
SELECT COUNT(*) FROM DELTA
FOR EACH STATEMENT 語句級觸發器則在INSERT語句執行完畢之后才執行一次觸發器中的動作
默認的觸發器是語句級觸發器,DELTA是一個關系名,其模式與Student相同,包含元組是INSERT語句增加的元組。
【案例:定義一個BEFORE行級觸發器,為教師表Teacher定義完整性規則“教授的工資不得低於4000元,如果低於4000元自動改為4000元”】
CREATE TRIGGER Insert_Or_Update_Sal /*對教師表插入或更行時激活觸發器*/
BEFORE INSERT OR UPDATE ON Teacher /*BEFORE觸發事件*/
REFERENCING NEW row AS newTuple
FOR EACH ROW /*行級觸發器*/
BEGIN
IF(newtuple.Job='教授') AND (newtuple.Sal<4000)
THEN newtuple.Sal:=4000; /*使用插入或更新操作后的新值*/
END IF;
END; /*觸發動作體結束*/
定義的是BEFORE觸發器,在插入和更新教師記錄前就可以按照觸發器的規則調整教授的工資,不必等插入后再檢查再調整
激活觸發器
觸發器的執行是由觸發事件激活,並由DBServer自動執行的。一個數據表上可能定義多個觸發器eg: 多個BEFORE觸發器、多個AFTER觸發器
=>同一個表上觸發器執行順序如下:
1.執行該表的BEFORE觸發器
2.激活觸發器的SQL語句
3.執行該表上的AFTER觸發器
對於同一個表上的多個BEFORE(AFTER)觸發器,遵循“誰先創建誰先執行”的原則,即按照觸發器創建的事件先后順序執行。