表和表之間可存在引用關系,這在抽象數據到表時,是很常見的。這種聯系是通過在表中創建外鍵(foreign key)來實現的。
比如一個訂單,可能關聯用戶表和產品表,以此來記錄誰買了什么產品。
約定兩個概念:
父表:被引用的表。
從表:表中有相應的外鍵引用父表中的字段。
示例:
CREATE TABLE parent (
id INT NOT NULL,
PRIMARY KEY (id)
) ENGINE=INNODB;
CREATE TABLE child (
id INT,
parent_id INT,
INDEX par_ind (parent_id),
FOREIGN KEY (parent_id)
REFERENCES parent(id)
ON DELETE CASCADE
) ENGINE=INNODB;
這里 parent
為父表,child
為從表。
外鍵關聯表的同步操作
當表和表之間建立起合適的關聯后, INSERT
和 UPDATE
操作會自動檢查所插入的記錄中指定的外鍵在相應表中是否存在;
建立外鍵時,可指定 ON UPDATE <action>
和 ON DELETE <action>
子語句來指定發生 UPDATE
和 DELETE
操作時,外鍵關聯表中數據該如何處理。MySQL 中支持五種處理(action):
CASCADE
:更新或刪除父表記錄時,自動更新或刪除從表中匹配的記錄。實際使用時注意不要在父表或從表中對同一列重復定義ON UPDATE CASCADE
。CASCADE 類型的操作不會激活觸發器 (triggers)。SET NULL
:更新或刪除父表中記錄時,將從表中匹配的記錄其外鍵設置為NULL
,前提時從表中該外鍵沒有指定為NOT NULL
。RESTRICT
:默認為該項。禁用父表中的更新或刪除操作,這與缺省ON DELETE
和ON UPDATE
子語句效果一樣。NO ACTION
:來自 SQL 標准中定義的一種操作,與上面RESTRICT
等效。一些數據庫支持延遲檢查(deferred check),NO ACTION
便是這種可延遲檢查的操作。在 MySQL 中,外鍵的檢查是及時的,所以NO ACTION
和RESTRICT
完全等效。SET DEFAULT
: MySQL 能夠解析識別該動作,但InnoDB
和NDB
引擎不支持。
添加外鍵
創建外鍵的語法
[CONSTRAINT [symbol]] FOREIGN KEY
[index_name] (col_name, ...)
REFERENCES tbl_name (col_name,...)
[ON DELETE reference_option]
[ON UPDATE reference_option]
reference_option:
RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT
文章開頭提到的訂單表示例:
CREATE TABLE product (
category INT NOT NULL, id INT NOT NULL,
price DECIMAL,
PRIMARY KEY(category, id)
) ENGINE=INNODB;
CREATE TABLE customer (
id INT NOT NULL,
PRIMARY KEY (id)
) ENGINE=INNODB;
CREATE TABLE product_order (
no INT NOT NULL AUTO_INCREMENT,
product_category INT NOT NULL,
product_id INT NOT NULL,
customer_id INT NOT NULL,
<span class="pl-k">PRIMARY KEY</span>(no),
INDEX (product_category, product_id),
INDEX (customer_id),
<span class="pl-k">FOREIGN KEY</span> (product_category, product_id)
<span class="pl-k">REFERENCES</span> product(category, id)
<span class="pl-k">ON</span> <span class="pl-k">UPDATE</span> CASCADE <span class="pl-k">ON DELETE</span> RESTRICT,
<span class="pl-k">FOREIGN KEY</span> (customer_id)
<span class="pl-k">REFERENCES</span> customer(id)
) ENGINE=INNODB;
所以這個關系里有兩個父表 customer
和 product
,一個從表 product_order
。
對現有表添加外鍵可使用如下的語句:
ALTER TABLE tbl_name
ADD [CONSTRAINT [symbol]] FOREIGN KEY
[index_name] (col_name, ...)
REFERENCES tbl_name (col_name,...)
[ON DELETE reference_option]
[ON UPDATE reference_option]
創建外鍵時可指定 [symbol]
,即給外鍵取一個名稱,這樣在其他操作時可以引用,比如刪除外鍵時。
刪除外鍵
同樣是通過 ALTER TABLE
語句來完成。
ALTER TABLE tbl_name DROP FOREIGN KEY fk_symbol;
刪除外鍵時,如果該外鍵在創建時取了名稱,名通過該名稱來刪除,如果沒有,則需要先查詢 MySQL 在創建該外鍵時自動生成的名稱 fk_symbol
是什么。可通過 SHOW CREATE TABLE <table_name>
來完成查詢。譬如:
mysql> SHOW CREATE TABLE dept_manager\G
*************************** 1. row ***************************
Table: dept_manager
Create Table: CREATE TABLE `dept_manager` (
`emp_no` int(11) NOT NULL,
`dept_no` char(4) COLLATE utf8mb4_general_ci NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`),
KEY `dept_no` (`dept_no`),
CONSTRAINT `dept_manager_ibfk_1` FOREIGN KEY (`emp_no`) REFERENCES `employees` (`emp_no`) ON DELETE CASCADE,
CONSTRAINT `dept_manager_ibfk_2` FOREIGN KEY (`dept_no`) REFERENCES `departments` (`dept_no`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
1 row in set (0.00 sec)