昨天我們在文章的最後面刪除了一筆資料,但是昨天沒有說的是,這樣的刪除指令其實蠻危險的。這就要牽扯到第二天提到的 Foreign Key 和 參考完整性 (Referential Integrity) 的概念。
在昨天的範例,我們有執行一個指令,明確的著名在資料關係表中哪一個欄位是primary key,現在我們要把剩下的也寫清楚:
Alter table customer add primary key (CustomerID);
Alter table foodtype add primary key (TypeID);
Alter table review add primary key (ReviewID);
-- 然後把昨天刪掉的 M006 加回來 然後新增幾筆資料 --
Insert into Menu (MenuID, Item, Price, TypeID, onSale) Values
("M006", "Magic Cookies", 99, NULL, False),
("M007", "Magic Candy", 88, "T003", False),
("M008", "Ice Lemon Tea", 60, "T002", False),
("M009", "Normal Candy", 5, "T003", False),
("M010", "Normal Cookies", 20, NULL, False);
這時候我們嘗試將T003 從 foodtype
關係表中刪除。結果是當我們想要把Menu關係表和 Foodtype
關係表重新 Join 起來的時候很多資料消失了! 那是因為 natural join 找不到相對應的 TypeID
。但是在資料庫當中,其實當我們要刪除一個指向其他表格中正在被使用的資料值的時候,資料庫管理系統應該要警告使用者這筆資料有其他資料把他當作參考。這就呼應了我們在第二天提到的參考完整性,這個概念可以避免資料刪除時影響到其他筆資料未來在join的時候產生的問題。
Delete From foodType Where TypeID='T003';
Select *
From menu natural join foodtype;
要定義這些foreign key,我們要先把資料補齊。被Reference 的關係表不可以有沒有出現的 Type, 但是Menu關係表中可以有尚未被定義的 Type。
Insert into FoodType (TypeID, TypeName) Values
("T003", "Candy"),
("T004", "Cookie");
Select * From Menu;
接下來就可以來定義
Alter table menu
Add Constraint Fk_TypeID
foreign key (TypeID) References foodtype(TypeID);
告訴資料庫,Menu 關係表中的 TypeID 會參照 foodtype 關係表中的 Type ID ,請做好管理兩個資料欄位的相關限制。同時我們將這個限制命名為 Fk_TypeID
。這時候如果我們要刪除 foodtype 的 T003
就會出現警告囉。
同時,如果我們想要新增一個不存在food type 關係表的食物,MySQL 同樣也會發出警告。
這時候你可能會覺得,有了這個限制之後操作起來不就代表說,我要去正確的地方,先將部分的資料刪除,然後才能回來將被參照的資料給刪除嗎? 這時候的資料庫管理系統就很聰明的能夠幫這些流程自動化。讓我們來更改一下剛才的設定: 首先我們要先把剛剛的限制先移除,然後重新修改連結foreign key的語法。
Alter table menu
drop foreign key Fk_TypeID;
Alter table menu
add Constraint Fk_TypeID foreign key (TypeID) References foodtype(TypeID)
On Delete Set NULL
On Update Cascade;
上面的指令應該蠻直接的,就是把他剛剛我們原本預設的 foreign key constraint 給刪除,接下來下面的指令相較於上面新增加了兩段,這兩段的語法一個是說如果當我 reference 的 foreign key 被刪除的時候資料庫系統應該要做什麼事情,另外一段是如果 foreign key 更新的時候我又應該做什麼事情。當我寫 Set Null
的時候就代表當我移除被 reference 關係表中的 foreign key ,其他使用這個foreign key 的關係表將會把該欄位的資料,自動設為空白值。同樣的如果使用的是 Cascade
(中文用級聯,我覺得很難懂,他的英文翻譯其實是瀑布,也就是由上層往下流動的意思),這個更動就會更迭。
Update foodtype set TypeID = "T009" where typeID = 'T003';
Select * from menu;
當我將 T003 改為 T009,menu 中的 T003 也會自動更新為 T009
delete from foodtype where typeID = 'T001';
Select * from menu;
當我們預設刪除的時候會自動帶入 Null
, 就會在這裡看到原本 T001 的位置已經變成空白值了。
-- 把資料庫刪掉的東西弄回來 --
Update foodtype set TypeID = "T003" where typeID = 'T009';
Insert into FoodType (TypeID, TypeName) Values ("T001", "Cake");
Update menu set TypeID= 'T001' where menuID = 'M001';
Update menu set TypeID= 'T001' where menuID = 'M003';
Update menu set TypeID= 'T004' where menuID = 'M006';
Update menu set TypeID= 'T004' where menuID = 'M010';
RDMS 不只可以透過 Foreign Key 來設定移除資料所產生的限制,同時也可以在定義資料綱要的時候給予限制,這邊我們舉個例子。
Alter table menu
ADD CONSTRAINT CHK_PriceRange CHECK (price > 0);
很直覺的,這個就是去修改資料綱要的 price 欄位,要求資料在輸入時價格不能小於 0。
今天的介紹中有很多是對 table 做修改,但是這些限制其實都可以在創建 table 的時候就直接定義。
註: 文章標題的 「正直」去的就是 Constraint 的直接翻譯