什么是外鍵約束
外鍵是表中的一列或一組列鏈接到另外一張表的一列或一組列。外鍵會(huì)在相關(guān)聯(lián)的表中起到約束作用,保證數(shù)據(jù)的一致性和實(shí)現(xiàn)一些級(jí)聯(lián)操作悍引。
如果在表A中建立外鍵趣斤,關(guān)聯(lián)到表B,那么表B為主表势腮,表A為從表捎拯。主表B中的對(duì)應(yīng)的列的更新或刪除會(huì)聯(lián)動(dòng)到外鍵所在的表A中的相應(yīng)的列的操作(具體的操作根據(jù)在表A中添加外鍵時(shí)的配置不同而不同)
即署照,建立外鍵的表為從表建芙,被外鍵關(guān)聯(lián)的表是主表岁钓。
創(chuàng)建外鍵約束時(shí)屡限,會(huì)針對(duì)本表中相應(yīng)的行自動(dòng)創(chuàng)建索引钧大。
外鍵的使用條件
- 外鍵只適用于InnoDB引擎啊央,MyISAM不支持瓜饥。
- 外鍵列必須建立了索引乓土,MySQL 4.1.2以后的版本在建立外鍵時(shí)會(huì)自動(dòng)創(chuàng)建索引狡相,但如果在較早的版本則需要顯式建立尽棕;
- 外鍵關(guān)系的兩個(gè)表的列必須是數(shù)據(jù)類型相似,也就是可以相互轉(zhuǎn)換類型的列氧敢,比如int和tinyint可以询张,而int和char則不可以份氧;
簡(jiǎn)單介紹
假設(shè)存在兩張表customers
和orders
唯袄。每一個(gè)custormer可以有0個(gè)或多個(gè)orders,同樣的蜗帜,每個(gè)order都屬于某一個(gè)customer恋拷。
可以看出custormers
和orders
表是一對(duì)多的關(guān)系。并且這個(gè)關(guān)系通過(guò)orders
表中的外鍵的customer_id
列來(lái)建立厅缺。
在oders
表中的custormer_id
列鏈接到customers
表中的id
主鍵列蔬顾。
此時(shí),customers
表通常被稱為主表(父表)湘捎,orders
表被稱為子表诀豁。
通常情況下,外鍵都是關(guān)聯(lián)到主表的主鍵列上面窥妇。
子表上面可以創(chuàng)建多個(gè)外鍵并關(guān)聯(lián)到多個(gè)主表的主鍵列上。
一旦外鍵約束就位沮焕。外鍵約束的列的值需要在主表的主鍵列上存在,或者為NULL(此時(shí)外鍵約束的action是SET NULL
)
例如,orders
表中的customer_id
列的值需要存在與customers
表的id
列上埋凯。oders
表中的多個(gè)行可以擁有相同的custormer_id
自引用的外鍵
有些時(shí)候,子表和主表可能是同一張表。這種情況下外鍵引用的是當(dāng)前表的主鍵
比如下面這張表employees
:
其中reportTo
字段是一個(gè)外鍵字段切端,它指向本表的主鍵列employeeNumber
。
這種關(guān)系允許employees
表存儲(chǔ)雇員與管理人員的關(guān)系結(jié)構(gòu)仪搔。每個(gè)雇員都有0個(gè)或1個(gè)上級(jí)煮嫌,且每個(gè)雇員可以擁有0個(gè)或多個(gè)下級(jí)。
此時(shí),reportTo
列上的外鍵就叫做遞歸或自引用外鍵。
創(chuàng)建外鍵的SQL語(yǔ)法
通過(guò)CREATE TABLE
或ALTER TABLE
創(chuàng)建外鍵的基本語(yǔ)法如下:
[CONSTRAINT [symbol]] FOREIGN KEY
[index_name] (col_name, ...)
REFERENCES tbl_name (col_name,...)
[ON DELETE reference_option]
[ON UPDATE reference_option]
首先在CONSTRAINT
關(guān)鍵字后面指定外鍵約束的名字阱缓。如果省略名字,那么MySQL會(huì)為此外鍵約束自動(dòng)創(chuàng)建一個(gè)名字秸妥。
接下來(lái)突雪,通過(guò)FOREIGN KEY
關(guān)鍵字來(lái)指定此外鍵的列督函,多個(gè)列的話用逗號(hào)分隔開(kāi)來(lái)宛篇,同樣的外鍵的名字也是可省略的吆倦。
第三步指定主表和主表上被引用到的列赛糟,多列用逗號(hào)分隔豆混。
最后鸵鸥,指定外鍵在子表和主表之間聯(lián)動(dòng)的動(dòng)作(action),這些動(dòng)作分為ON DELETE
和ON UPDATE
根时。reference_option
表示子表中此外鍵關(guān)聯(lián)的列所采取的行動(dòng)忘苛,當(dāng)主表中被引用的列被刪除(ON DELETE
)或更新(ON UPDATE
)時(shí)。
MySQL有5種reference options:CASCADE
, SET NULL
, NO ACTION
, RESTRICT
, and SET DEFAULT
:
-
CASCADE
:級(jí)聯(lián)操作纸镊,如果父表中的一行被刪除或更新,子表會(huì)自動(dòng)跟著刪除或更新厌杜。 -
SET NULL
:如果父表中的行被刪除或更新肺孤,子表中相應(yīng)的列的值被設(shè)置為NULL
半等。 -
RESTRICT
:如果父表中被外鍵引用的列的值在子表中存在相應(yīng)的行與之匹配,MySQL拒絕父表的這個(gè)刪除或更新操作羡儿。 -
NO ACTION
:同RESTRICT
-
SET DEFAULT
:MySQL的語(yǔ)法分析器可以識(shí)別厨相,但是InnoDB和NDB引擎不支持府适。
實(shí)際上朋贬,MySQL支持三種actions:RESTRICT
, CASCADE
and SET NULL
。
如果沒(méi)有設(shè)置ON DELETE
和ON UPDATE
另锋,那么默認(rèn)的action是RESTRICT
亡鼠。
開(kāi)始實(shí)驗(yàn)
下面通過(guò)實(shí)際的操作來(lái)體驗(yàn)外鍵的功能。
創(chuàng)建兩張表custormers
和orders
CREATE TABLE customers(
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL
) ENGINE=INNODB;
CREATE TABLE orders(
id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT,
order_info VARCHAR(255)
) ENGINE=INNODB;
RESTRICT & NO ACTION actions
在orders
表中創(chuàng)建外鍵震嫉,使用默認(rèn)的RESTRICT
action。
ALTER TABLE orders
ADD FOREIGN KEY (customer_id)
REFERENCES customers(id);
未指定action時(shí)昔穴,默認(rèn)
ON DELETE
和ON UPDATE
都是RESTRICT
在customers
表中插入數(shù)據(jù):
INSERT INTO customers (name) values ('andy'),('jerry');
此時(shí)customser
表中的數(shù)據(jù)為:
id | name |
---|---|
1 | andy |
2 | jerry |
在orders
表中插入新的行:
INSERT INTO orders (customer_id,order_info) values (1,'info');
插入成功,因?yàn)閕d為1的customer_id存在于customers
表中拓售。
那么接下來(lái)在orders
表中插入一條customer_id不存在的數(shù)據(jù)看看會(huì)發(fā)生什么:
INSERT INTO orders (customer_id,order_info) values (3,'info');
出現(xiàn)了如下的報(bào)錯(cuò)信息:
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test`.`orders`, CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`))
那么接下來(lái)更新一下customers
表中id為1的行試試看:
UPDATE customers set id = 5 where id = 1;
出現(xiàn)了報(bào)錯(cuò):
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test`.`orders`, CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`))
因?yàn)?code>RESTRICTaction币砂,如果子表中存在父表中外鍵約束引用到的列的值边锁,那么mysql會(huì)阻止了父表的更新和刪除操作。
下面的更新就不會(huì)出錯(cuò)浑吟,因?yàn)閕d為2的customer_id不存在于oders
表中洒放。
UPDATE customers set id = 5 where id = 2;
CASCADE
action
刪除外鍵約束
需要使用兩條命令來(lái)刪除:
//刪除外鍵約束
ALTER TABLE orders DROP FOREIGN KEY `orders_ibfk_1`;
//刪除創(chuàng)建外鍵約束時(shí)自動(dòng)創(chuàng)建的索引
ALTER TABLE orders DROP INDEX `customer_id`;
注意刪除外鍵約束的時(shí)候蛉鹿,使用語(yǔ)句
ALTER TABLE example_table DROP FOREIGN KEY `constraint_name`;
這里的constraint_name
是外鍵約束的名字,而不是外鍵的名字往湿,如果創(chuàng)建約束的時(shí)候沒(méi)有指定名字妖异,那么可以通過(guò)SHOW CREATE TABLE example_table
命令查看。
同時(shí)领追,上面的命令刪除了外鍵約束并不會(huì)同步刪除創(chuàng)建外鍵約束是的對(duì)應(yīng)列的索引他膳,所以需要額外的一條命令去刪除它。
創(chuàng)建新的外鍵約束绒窑,使用CASCADE
action:
ALTER TABLE orders
ADD FOREIGN KEY (customer_id)
REFERENCES customers(id)
ON UPDATE CASCADE
ON DELETE CASCADE;
此時(shí)orders
表中的數(shù)據(jù)如下:
id | customer_id | order_info |
---|---|---|
1 | 1 | info |
將custorms
表中的id為1的行改為10:
UPDATE customers set id = 10 where id = 1;
更新成功之后矩乐,查看orders
表中的數(shù)據(jù):
id | customer_id | order_info |
---|---|---|
1 | 10 | info |
發(fā)現(xiàn)其customer_id
列的值也同步更新為了10
接下來(lái)刪除customsers
表中id為10的行:
DELETE FROM customers where id = 10;
執(zhí)行成功之后查看orders
表中的數(shù)據(jù)為空,其與主表關(guān)聯(lián)的行數(shù)據(jù)也被刪除了。
SET NULL
action
刪除外鍵約束
需要使用兩條命令來(lái)刪除:
//刪除外鍵約束
ALTER TABLE orders DROP FOREIGN KEY `orders_ibfk_1`;
//刪除創(chuàng)建外鍵約束時(shí)自動(dòng)創(chuàng)建的索引
ALTER TABLE orders DROP INDEX `customer_id`;
創(chuàng)建新的外鍵約束散罕,使用CASCADE
action:
ALTER TABLE orders
ADD FOREIGN KEY (customer_id)
REFERENCES customers(id)
ON UPDATE SET NULL
ON DELETE SET NULL;
這里注意創(chuàng)建外鍵約束的列
customer_id
不能使用NOT NULL
語(yǔ)句分歇,不然無(wú)法創(chuàng)建SET NULL
action的外鍵約束
往orders
插入新的數(shù)據(jù)(上個(gè)步驟的刪除行操作已經(jīng)將orders
表的數(shù)據(jù)刪除了)
INSERT INTO orders (customer_id,order_info) values (5,'info');
id | customer_id | order_info |
---|---|---|
1 | 5 | info |
更新customers
表中id為5個(gè)行,將id改為50:
UPDATE customers set id = 50 where id = 5;
查看orders
表:
id | customer_id | order_info |
---|---|---|
1 | NULL | info |
原先customer_id
為5的那一行數(shù)據(jù)欧漱,現(xiàn)在值變?yōu)榱?code>NULL职抡,這是因?yàn)?code>ON UPDATE SET NULL起作用了
往customsers
和orders
表中都插入新的測(cè)試數(shù)據(jù)
INSERT INTO customers (id,name) values (8,'andy');
INSERT INTO orders (customer_id,order_info) values (8,'info');
現(xiàn)在customsers
表和orders
表的數(shù)據(jù)分別如下:
id | name |
---|---|
8 | andy |
50 | jerry |
id | customer_id | order_info |
---|---|---|
1 | NULL | info |
4 | 8 | info |
刪除customers
表中新增的id為8的數(shù)據(jù):
DELETE FROM customers where id = 8;
此時(shí)orders
表中的相對(duì)應(yīng)的行的customer_id
列的值變?yōu)榱?code>NULL:
id | customer_id | order_info |
---|---|---|
1 | NULL | info |
4 | NULL | info |
這是因?yàn)橥怄I約束ON DELETE SET NULL
action 起了作用
禁用外鍵檢查
有些時(shí)候當(dāng)我們需要從一個(gè)表中導(dǎo)入數(shù)據(jù)是,如果存在外鍵误甚,那么導(dǎo)入和修改的順序就不能錯(cuò)亂缚甩,必須嚴(yán)格遵循先導(dǎo)入主表然后再導(dǎo)入子表的順序。此時(shí)可以通過(guò)改變變量的形式來(lái)臨時(shí)禁用外鍵檢查窑邦。
禁用外鍵檢查:
SET foreign_key_checks = 0;
啟用外鍵檢查:
SET foreign_key_checks = 1;
參考
An Essential Guide to MySQL Foreign Key By Practical Examples
Using FOREIGN KEY Constraints
mysql 外鍵(foreign key)的詳解和實(shí)例