寫在前面:本篇博客大部分內(nèi)容參考數(shù)據(jù)庫系統(tǒng)概念(本科教學(xué)版)第四章的一些尾巴,然后開始講第六章的關(guān)系代數(shù)
筆者接下來的代碼示例會主要在SQL Server數(shù)據(jù)庫中測試
在開始今天的摸魚大業(yè)之前,讓我們構(gòu)造一些簡單表
-- 執(zhí)行下面的語句構(gòu)造表
CREATE TABLE country(
country_id INTEGER PRIMARY KEY ,
country_name VARCHAR(20)
);
CREATE TABLE person(
person_id INTEGER PRIMARY KEY ,
name VARCHAR(20),
country_id INTEGER FOREIGN KEY REFERENCES country(country_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
INSERT INTO country (country_id, country_name) VALUES (
1, 'China'
);
INSERT INTO country (country_id, country_name) VALUES (
2, 'English'
);
INSERT INTO country (country_id, country_name) VALUES (
3, 'America'
);
INSERT INTO person (person_id, name, country_id) VALUES (
1, 'Sunny', 1
);
INSERT INTO person (person_id, name, country_id) VALUES (
2, 'Robbin', 2
);
INSERT INTO person (person_id, name, country_id) VALUES (
3, 'Jane', 3
);
級聯(lián)操作
在指定外鍵以后埋涧,由于存在完整性約束河狐,所以在執(zhí)行刪除或更新的時候由于語句可能會破壞完整性約束而執(zhí)行失敗布持。因此可以在定義外鍵的時候聲明為級聯(lián)刪除和級聯(lián)更新(是一種對違反參照完整性約束時的處理方式)
-
使用方式
CREATE table 表名( ... FOREGIN KEY (字段序列) REFERENCES 表名(字段序列) ON DELETE CASCADE ON UPDATE CASCADE, ... )
-
級聯(lián)刪除(ON DELETE CASCADE)
- 級聯(lián)刪除是在定義外鍵時指定的筐眷,但是卻會在執(zhí)行刪除語句時產(chǎn)生影響
- 舉個栗子
- 我們先不指定級聯(lián)
DROP TABLE person; DROP TABLE country; CREATE TABLE country( country_id INTEGER PRIMARY KEY , country_name VARCHAR(20) ); CREATE TABLE person( person_id INTEGER PRIMARY KEY , name VARCHAR(20), country_id INTEGER FOREIGN KEY REFERENCES country(country_id) ); INSERT INTO country (country_id, country_name) VALUES ( 1, 'China' ); INSERT INTO country (country_id, country_name) VALUES ( 2, 'English' ); INSERT INTO country (country_id, country_name) VALUES ( 3, 'America' ); INSERT INTO person (person_id, name, country_id) VALUES ( 1, 'Sunny', 1 ); INSERT INTO person (person_id, name, country_id) VALUES ( 2, 'Robbin', 2 ); INSERT INTO person (person_id, name, country_id) VALUES ( 3, 'Jane', 3 );
- 然后執(zhí)行下面的刪除操作
-- 下面我們試圖刪除中國的信息肿孵,但是在person表里有一條數(shù)據(jù)引用了中國澳眷,所以因為參照完整性約束的存在胡嘿,所以這條語句會執(zhí)行失敗 DELETE country WHERE country_id = 1
- 接下來我們重新構(gòu)造一遍(當(dāng)然直接用DDL語句更新也是可以的)--并在構(gòu)造person表時指定了級聯(lián)刪除
DROP TABLE person; DROP TABLE country; CREATE TABLE country( country_id INTEGER PRIMARY KEY , country_name VARCHAR(20) ); CREATE TABLE person( person_id INTEGER PRIMARY KEY , name VARCHAR(20), country_id INTEGER FOREIGN KEY REFERENCES country(country_id) ON DELETE CASCADE ); INSERT INTO country (country_id, country_name) VALUES ( 1, 'China' ); INSERT INTO country (country_id, country_name) VALUES ( 2, 'English' ); INSERT INTO country (country_id, country_name) VALUES ( 3, 'America' ); INSERT INTO person (person_id, name, country_id) VALUES ( 1, 'Sunny', 1 ); INSERT INTO person (person_id, name, country_id) VALUES ( 2, 'Robbin', 2 ); INSERT INTO person (person_id, name, country_id) VALUES ( 3, 'Jane', 3 );
- 然后再次執(zhí)行下面的刪除操作
-- 此時執(zhí)行會發(fā)現(xiàn)語句成功執(zhí)行了,不但刪除了中國的信息钳踊,連帶person表中引用了中國信息的所有數(shù)據(jù)都被刪除了 DELETE country WHERE country_id = 1
- 我們先不指定級聯(lián)
- 上面的例子便很好的說明了級聯(lián)刪除的作用衷敌。如果我們視圖刪除外鍵參照鍵所在表(此處為country表)的某條數(shù)據(jù)A(此處是中國的信息),而這條數(shù)據(jù)又被外鍵所在表的一條或多條數(shù)據(jù)B所關(guān)聯(lián)(此處person表中Sunny的country_id關(guān)聯(lián)了country表中中國的id)拓瞪。在指定了級聯(lián)刪除的情況下缴罗,刪除A會連帶著刪除所有滿足條件的B
- 當(dāng)然在實際使用的時候用的還是比較少的,因為參照完整性約束在一定程度上可以防止數(shù)據(jù)的誤刪除祭埂,對數(shù)據(jù)庫的完整性起了一定的保護作用面氓,如果指定了級聯(lián)刪除,這層保護就失效了蛆橡。所以還是視情況而用
-
級聯(lián)更新(ON UPDATE CASCADE)
- 類似的舌界,級聯(lián)更新和級聯(lián)刪除一樣,如果我們更新時違反了完整性約束泰演,同樣更新操作不被拒絕呻拌,而是級聯(lián)更新
- 舉個栗子(我們在上面操作的基礎(chǔ)上執(zhí)行,上面構(gòu)造時指定了級聯(lián)刪除睦焕,但是沒指定級聯(lián)更新)
-- 我們試圖執(zhí)行下面的更新操作柏锄,我們把修改English的country_id, 但是由于person表中還有數(shù)據(jù)的country_id=2,如果下面的更新成功執(zhí)行复亏,則會導(dǎo)致person表中存在country_id=2的數(shù)據(jù)趾娃,而country中卻沒有對應(yīng)數(shù)了,違反參照完整性約束缔御,故下面的語句執(zhí)行失敗 UPDATE country SET country_id = 4 WHERE country_id = 2
- 同樣的抬闷,我們重新構(gòu)造一下,此時指定級聯(lián)更新
DROP TABLE person; DROP TABLE country; CREATE TABLE country( country_id INTEGER PRIMARY KEY , country_name VARCHAR(20) ); CREATE TABLE person( person_id INTEGER PRIMARY KEY , name VARCHAR(20), country_id INTEGER FOREIGN KEY REFERENCES country(country_id) ON DELETE CASCADE ON UPDATE CASCADE ); INSERT INTO country (country_id, country_name) VALUES ( 1, 'China' ); INSERT INTO country (country_id, country_name) VALUES ( 2, 'English' ); INSERT INTO country (country_id, country_name) VALUES ( 3, 'America' ); INSERT INTO person (person_id, name, country_id) VALUES ( 1, 'Sunny', 1 ); INSERT INTO person (person_id, name, country_id) VALUES ( 2, 'Robbin', 2 ); INSERT INTO person (person_id, name, country_id) VALUES ( 3, 'Jane', 3 );
- 此時再執(zhí)行一下上面的更新語句
-- 由于指定了級聯(lián)更新,所以會發(fā)現(xiàn)下面的語句執(zhí)行成功了笤成,不但更改了country表中的數(shù)據(jù)评架,連帶著person表中的數(shù)據(jù)也一并更新了 UPDATE country SET country_id = 4 WHERE country_id = 2
- 上面就是級聯(lián)更新的效果
-
另一類對違反完整性約束的處理
- SET DEFAULT
- 一旦違反完整性約束,就將參照域(此處為country_id)設(shè)置為默認(rèn)值
- SET NULL
- 一旦違反完整性約束炕泳,就將參照域(此處為country_id)設(shè)置為NULL
- 舉個栗子
- 執(zhí)行下面的構(gòu)造
DROP TABLE person; DROP TABLE country; CREATE TABLE country( country_id INTEGER PRIMARY KEY , country_name VARCHAR(20) ); CREATE TABLE person( person_id INTEGER PRIMARY KEY , name VARCHAR(20), country_id INTEGER FOREIGN KEY REFERENCES country(country_id) ON DELETE SET NULL ); INSERT INTO country (country_id, country_name) VALUES ( 1, 'China' ); INSERT INTO country (country_id, country_name) VALUES ( 2, 'English' ); INSERT INTO country (country_id, country_name) VALUES ( 3, 'America' ); INSERT INTO person (person_id, name, country_id) VALUES ( 1, 'Sunny', 1 ); INSERT INTO person (person_id, name, country_id) VALUES ( 2, 'Robbin', 2 ); INSERT INTO person (person_id, name, country_id) VALUES ( 3, 'Jane', 3 );
- 然后執(zhí)行下面的刪除操作
-- 下面的刪除操作成功執(zhí)行纵诞,但不是級聯(lián)刪除,而是把person表中原來country_id=1的數(shù)據(jù)的country_id都設(shè)成了NULL DELETE country WHERE country_id = 1
- 執(zhí)行下面的構(gòu)造
- SET DEFAULT
延遲檢查
這是由于數(shù)據(jù)庫默認(rèn)是在執(zhí)行每一條SQL語句的時候都進行完整性約束的檢查培遵,導(dǎo)致有些操作無法進行浙芙。延遲操作就將完整性約束的檢查延遲到了事務(wù)結(jié)束的時候檢查(大多數(shù)數(shù)據(jù)庫不支持,比如SQL Server籽腕, 但Oracle數(shù)據(jù)支持)
由于不常用嗡呼,SQL Server也不支持,這里就講一下概念皇耗,不舉實際的栗子了南窗。
-
假設(shè)上面的例子表中沒有指定延遲檢查
- 執(zhí)行下面的語句
-- 執(zhí)行下面兩條語句是會出錯的,因為插入第一條數(shù)據(jù)的時候郎楼,由于完整性約束的存在万伤,要求country表中要有country_id=4的數(shù)據(jù),但是這個數(shù)據(jù)目前還不存在(所以只要先執(zhí)行第二條語句呜袁,這兩個語句才能成功執(zhí)行) INSERT INTO person (person_id, name, country_id) VALUES ( 4, 'Jerry', 4 ); INSERT INTO country (country_id, country_name) VALUES ( 4, 'France' );
-
而如果指定了延遲檢查呢
- 執(zhí)行下面語句(下面兩個語句處于同一個事務(wù)中)
-- 由于是延遲檢查壕翩,所以兩條數(shù)據(jù)都插入完,執(zhí)行commit傅寡,事務(wù)結(jié)束時才進行完整性約束的檢查放妈,此時就不會出錯,可以正常插入 INSERT INTO person (person_id, name, country_id) VALUES ( 4, 'Jerry', 4 ); INSERT INTO country (country_id, country_name) VALUES ( 4, 'France' ); COMMIT
雖然SQL標(biāo)準(zhǔn)中有這個概念荐操,但是大多數(shù)數(shù)據(jù)庫沒有提供支持芜抒,并且不常用