資料庫知識整理
主要內容
資料的完整性和安全性是兩個既有聯絡又不盡相同的概念。資料的完整性是為了防止資料庫中存在不符合語義的資料;資料的安全性是保護資料庫免受惡意破壞和非法存取。
下面我們將進一步瞭解資料庫的完整性。
實體完整性
關係模型的實體完整性是關於元組(記錄)唯一性的定義,在CREATE TABLE 中用PRIMARY KEY 來實現。
當主碼由單個屬性構成時,可使用列級約束條件或表級約束條件;而當主碼由多個屬性構成時,只能使用表級約束條件。
用PROMARY KEY 短語定義了關係的主碼後,每當使用者程式對基本表插入一條記錄或對主碼列進行更新操作時,關係資料庫管理系統
但這種全表掃描效率極低,因而關係資料庫管理系統一般會在主碼上自動建立一個索引。
小拓展:索引的應用
假設在Student表中,學生的姓名Sname為主碼。
在基本表中,學生的記錄是無序地逐條插入的,現在我們先後插入了Tom、Amy、Jenny、Belly、Jack和Anne的記錄。
(1)如果我們要查詢Anne的資料,首先得找到“Anne”所在的元組,然後才能輸出她的資料,而這樣我們必須得遍歷6個元組,因為Anne的記錄在最後一行元組。
(2)如果我們要查詢Alex的資料,顯而易見,Alex並不在Student表中,但計算機是不知道的,它必須得一個個去查去對比。遍歷6個元組
(3)如果我們要插入新同學Ben的資料,計算機依然要遍歷6個元組,確定無重複後才能插入記錄。
當我們使用索引後,Student表中的記錄順序就會變成Amy、Anne、Belly、Jack、Jenny和Tom,按照字母表的順序。
接著我們再重複一遍上面的操作:
(1)查詢Anne的資料:根據索引,“A”有Amy和Anny,“A-n”只有Anne,可以開始查詢,一共遍歷2個元組。
(2)查詢Alex的資料:根據索引,“A”有Amy和Anne,“A-l”沒有對應的元組,無法查詢,一共遍歷2個元組。
(3)插入Ben的資料:根據索引,“B”只有Belly,“B-e”也還是Belly,而“B-e-n”無重複值,可以插入,一共遍歷1個元組
從這個簡單的例子我們就可以看出索引對於查詢效率的重大意義,以後有時間我會圍繞“索引”再寫一篇博文。
參照完整性
關係模型的參照完整性在CREATE TABLE 中用FOREIGN KEY 短語定義哪些列為外碼,用REFERENCES 短語指明這些外碼參照哪些表的主碼。
參照完整性將兩個表中的相應元組聯絡起來了。因此,對參照表或被參照表進行增、刪、改操作時,都有可能會破壞參照完整性,因此我們必須對違約操作進行處理:
(1)拒絕執行(NO ACTION)—— 預設策略;
(2)級聯操作(CASCADE):當刪除或修改被參照表的一個元組導致與參照表不一致時,同時刪除或修改參照表中所有相關的元組;
(3)設定為空值(SET NULL):當刪除或修改被參照表的一個元組導致與參照表不一致時,將參照表中所有相關的元組的對應屬性值設為空值。
//顯式說明參照完整性的違約處理示例:
CREATE TABLE SC
(Sno CHAR(9),
Cno CHAR(4),
Grade SMALLINT,
PRIMARY KEY(Sno,Cno), /*定義表級實體完整性*/
FOREIGN KEY(Sno) REFERENCES Student(Sno)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY(Cno) REFERENCES Course(Cno)
ON DELETE NO ACTION /*當刪除Course表中的元組造成與SC表不一致時,拒絕刪除*/
ON UPDATE CASCADE /*當更新Course表中的元組造成與SC表不一致時,級聯更新*/
);
使用者定義的完整性
1. 屬性上的約束條件
在CREATE TABLE 中定義屬性的同時定義約束條件:
(1)列值非空(NOT NULL);
(2)列值唯一(UNIQUE);
(3)檢查列值是否滿足一個條件表示式(CHECK短語)。
不滿足約束條件的操作將被拒絕執行。
//Student表的Ssex只允許取“男”或“女”:
CREATE TABLE Student(
Sno CHAR(9) PRIMARY KEY,
Sname CHAR(20) UNIQUE,
Ssex CHAR(2) NOT NULL CHECK(Ssex IN ('男','女')),
Sage SMALLINT,
Sdept VARCHAR(20)
);
2. 元組上的約束條件
同屬性值限制相比,元組級的限制可以設定不同屬性之間取值的相互約束條件。
//當學生性別為男生時,其名字不能以“Ms.”開頭:
CREATE TABLE Student(
Sno CHAR(9) PRIMARY KEY,
Sname CHAR(20) UNIQUE,
Ssex CHAR(2) NOT NULL CHECK(Ssex IN ('男','女')),
Sage SMALLINT,
Sdept VARCHAR(20),
CHECK (Ssex = '女' OR Sname NOT LIKE 'Ms.%')
);
完整性約束命名子句
通俗點講就是給一個約束條件起名字,方便增、刪和改。需要先提一下的是,對於約束條件並沒有ALTER短語,我們只能先把舊的刪掉再插入一個新的。
//學號在20170000~20170499之間,姓名不能取空值,年齡小於30,性別只能是“男”或“女”:
CREATE TABLE Student(
Sno NUMERIC(9)
CONSTRAINT C1 CHECK (Sno BETWEEN 20170000 AND 20170499),
Sname CHAR(20)
CONSTRAINT C2 NOT NULL,
Ssex CHAR(2)
CONSTRAINT C3CHECK(Ssex IN ('男','女')),
Sage NUMERIC(2),
CONSTRAINT C4CHECK(Sage < 30),
CONSTRAINT StudentPKey PRIMARY KEY(Sno)
);
//去掉對性別的限制:
ALTER TABLE Student
DROP CONSTRAINT C3;
//增加新的約束條件:
ALTER TABLE Student
ADD CONSTRAINT C5 CHECK(Sage < 25);
域中的完整性約束條件
域是一組具有相同資料型別的值的集合,類似於列舉型別。
//建立一個性別域並宣告域的取值範圍:
CREATE DOMAIN GenderDomain CHAR(2)
CONSTRAINT GD CHECK (VALUE IN ('男','女'));
斷言
在SQL中可以用CREATE ASSERTION 語句來指定更具一般性的約束。
斷言建立以後,任何對斷言中所涉及關係的操作都會觸發關係資料庫管理系統對斷言的檢查,任何使斷言不為真值的操作都會被拒絕執行。
//限制資料庫課程最多60名學生選修:
CREATE ASSERTION ASSE_SC_NUM
CHECK(60 >= (
SELECT COUNT(*)
FROM Course,SC
WHERE SC.Cno = Course.Cno AND Course.Cname = '資料庫')
);
每當學生選修課程時,將在SC表中插入一條元組(Sno,Cno,NULL),ASSE_SC_NUM斷言被觸發檢查。如果選修資料庫課程的人數已經超過60人,CHECK子句返回值為“假”,對SC表的插入操作會被拒絕。
//刪除對資料庫課程的斷言:
DROP ASSERTION ASSE_SC_NUM;
觸發器
觸發器(Trigger)是使用者定義在基本表上的一類由觸發事件驅動的操作過程。
觸發器被定義後會儲存在資料庫伺服器中。任何使用者對錶的增、刪、改操作均由伺服器自動啟用相應的觸發器,在關係資料庫管理系統核心層進行集中的完整性控制。
觸發器類似於約束,但比約束更加靈活。
需要注意的是,觸發器在SQL99後才寫入SQL標準,但是很多關係資料庫管理系統在統一標準之前就產生了各自的觸發器,因而這些觸發器的語法各不相同、互不相容。上機實驗時需要留意所用系統的觸發器說明。
1. 定義觸發器
觸發器又叫“事件-條件-動作”規則(event-condition-action)。當特定的觸發事件發生時,對規則的條件進行檢查,若條件成立,規則中的動作將被執行,反之拒絕執行。若不設定規則的動作條件,觸發事件發生時立即執行動作。
一般格式:
CREATE TRIGGER <觸發器名>
{BEFORE|AFTER} <觸發事件> ON <目標基本表名>
REFERENCING {NEW|OLD} {ROW|TABLE} AS <臨時變數名>
FOR EACH {ROW|STATEMENT}
[WHEN <觸發條件>]
<觸發動作體>
我們可以看到,在觸發器的一般格式中有多個的可選項,下面會一個個介紹:
(1)CREATE TRIGGER <觸發器名>
只有建立基本表的使用者才有許可權在表上建立觸發器,並且一個表上只能建立一定數量的觸發器,具體數量由關係資料庫管理系統決定;
同一模式(名稱空間)中,觸發器名必須唯一,且必須與目標基本表位於同一模式。
(2){BEFORE|AFTER} <觸發事件> ON <目標基本表名>
{BEFORE|AFTER} 指示觸發器被啟用的時機,在觸發事件執行之前或之後;
觸發事件可以是INSERT、DELETE或UPDATE,也可以是這幾個事件的組合,還可以是UPDATE OF <觸發列, ...>,即進一步制定修改哪些屬性列時啟用觸發器;
ON <目標基本表名> 的意思就很明顯了。
(3)REFERENCING {NEW|OLD} {ROW|TABLE} AS <臨時變數名>
將觸發事件執行前後,元組或表的內容儲存在<臨時變數名>中,以便在觸發動作體中使用。
需要注意的是,只有行級觸發器才能在觸發動作體中使用NEW|OLD變數,而在語級觸發器中禁止使用。
/*現在看不懂上面幾個新名詞不要緊,下面會進一步說明*/
(4)FOR EACH {ROW|STATEMENT}
該語句可將觸發器定義為行級觸發器(ROW)或語級觸發器(STATEMENT)。
當觸發事件僅涉及基本表中的一行元組或元組中的一個屬性值,我們可以將觸發器定義為行級觸發器。每當一個元組被UPDATE(或其他操作),且觸發條件成立時,觸發動作體就會執行一次,即一個個觸發,一個個操作;
當觸發事件涉及整體基本表的改動時,如插入或刪除一行元組,則將觸發器定義為語級觸發器。所有INSERT或DELETE語句執行後,且觸發條件成立時,觸發動作體才執行一次,即所有觸發後,來一次總操作。
(5)[WHEN <觸發條件>]
觸發條件為可選語句,設定後在執行觸發動作體前將進行條件判斷,不設定則直接執行。
(6)<觸發動作體>
觸發動作體可以是INSERT、DELETE、UPDATE或PL/SQL過程塊。
下面通過三個例子體會一下~
//當對錶SC的Grade屬性進行修改時,若分數增加了10%,則將此次操作記錄到另一個表SC_U(Sno,Cno,Oldgrade,Newgrade)中:
CREATE TRIGGER SC_T
AFTER UPDATE OF Grade ONSC
REFERENCING
OLD ROW AS OldTuple,
NEW ROW AS NewTuple
FOR EACH ROW /*定義為行級觸發器*/
WHEN (NewTuple.Grade >= 1.1 * OldTuple.Grade)
INSERT INTOSC_U(Sno,Cno,Oldgrade,Newgrade)
VALUES(OldTuple.Sno,OldTuple.Cno,OldTuple.Grade,NewTuple.Grade)
//將每次對錶Student的插入操作所增加的學生個數記錄到C中:
CREATE TRIGGER Student_Count
AFTER INSERT ON Student
REFERENCING
NEW TABLE AS OldTable
FOR EACH STATEMENT /*定義為語級觸發器*/
INSERT INTO OldTable(Numbers)
SELECT COUNT(*) FROM OldTable
//定義一個BEFORE行級觸發器,為Teacher表定義完整性規則“教授的工資不得低於4000元,如果低於4000元,自動補為4000”:
CREATE TRIGGER Insert_OR_Update_Sal
BEFORE INSERT OR UPDATE ONTeacher
REFERENCING
NEW ROW AS NewTuple
FOR EACH ROW
BEGIN /*PL/SQL過程塊開端*/
IF (NewTuple.Job = '教授') AND (NewTuple.Sal < 4000)
THEN NewTuple.Sal = 4000;
END IF; /*IF語句結束*/
END; /*PL/SQL過程塊結尾*/
//刪除觸發器:
DROP TRIGGERInsert_OR_Update_Sal ON Teacher;
2. 觸發器啟用順序
先執行BEFORE觸發器再執行AFTER觸發器。同種觸發器中,遵循“先建立先執行”的原則。有些關係資料庫管理系統會按照觸發器名稱的字母排列順序執行。