MySQL 的外鍵約束實作在以下幾個主要方面與 SQL 標準不同:
如果父資料表中有數個具有相同參考鍵值的列,
InnoDB執行外鍵檢查時,會假設其他具有相同鍵值的父列不存在。例如,如果您定義RESTRICT類型的約束,並且有一個子列與多個父列相關聯,則InnoDB不允許刪除任何父列。以下範例說明了這一點:mysql> CREATE TABLE parent ( -> id INT, -> INDEX (id) -> ) ENGINE=InnoDB; Query OK, 0 rows affected (0.04 sec) mysql> CREATE TABLE child ( -> id INT, -> parent_id INT, -> INDEX par_ind (parent_id), -> FOREIGN KEY (parent_id) -> REFERENCES parent(id) -> ON DELETE RESTRICT -> ) ENGINE=InnoDB; Query OK, 0 rows affected (0.02 sec) mysql> INSERT INTO parent (id) -> VALUES ROW(1), ROW(2), ROW(3), ROW(1); Query OK, 4 rows affected (0.01 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> INSERT INTO child (id,parent_id) -> VALUES ROW(1,1), ROW(2,2), ROW(3,3); Query OK, 3 rows affected (0.01 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> DELETE FROM parent WHERE id=1; ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE RESTRICT)如果
ON UPDATE CASCADE或ON UPDATE SET NULL遞迴更新在相同串聯期間先前更新的相同資料表,它的行為會類似於RESTRICT。這表示您無法使用自我參照的ON UPDATE CASCADE或ON UPDATE SET NULL作業。這是為了防止因串聯更新而導致無限迴圈。另一方面,自我參照的ON DELETE SET NULL是可行的,自我參照的ON DELETE CASCADE也是如此。串聯操作的巢狀深度不得超過 15 層。在插入、刪除或更新多個列的 SQL 語法中,外鍵約束(如同唯一約束)會逐列檢查。
InnoDB在執行外鍵檢查時,會在它必須檢查的子記錄或父記錄上設定共用列級鎖定。MySQL 會立即檢查外鍵約束;檢查不會延遲到交易提交。根據 SQL 標準,預設行為應該是延遲檢查。也就是說,約束只有在處理完整個 SQL 語法後才會進行檢查。這表示不可能使用外鍵刪除參考自身的列。沒有任何儲存引擎(包括
InnoDB)會辨識或強制執行參考完整性約束定義中使用的MATCH子句。使用明確的MATCH子句不會產生指定的效果,並且會導致ON DELETE和ON UPDATE子句被忽略。應該避免指定MATCH。SQL 標準中的
MATCH子句控制當與參考資料表中的主鍵比較時,如何處理複合(多欄)外鍵中的NULL值。MySQL 本質上實作了MATCH SIMPLE定義的語意,允許外鍵為全部或部分NULL。在這種情況下,即使包含此外鍵的(子資料表)列與參考(父)資料表中沒有任何列相符,也可以插入該列。(可以使用觸發器來實作其他語意。)參考非
UNIQUE鍵的FOREIGN KEY約束不是標準 SQL,而是InnoDB擴充功能,現在已棄用,而且必須透過設定restrict_fk_on_non_standard_key來啟用。您應該預期在未來版本的 MySQL 中移除對使用非標準鍵的支援,並且現在就開始從這些鍵遷移。NDB儲存引擎需要每個做為外鍵參考的欄位上都有明確的唯一鍵(或主鍵),這符合 SQL 標準。對於不支援外鍵的儲存引擎(例如
MyISAM),MySQL 伺服器會解析並忽略外鍵規格。MySQL 會解析但忽略(如 SQL 標準中定義)在定義參考做為欄位規格一部分的「“內嵌
REFERENCES規格”」。MySQL 僅接受指定為單獨FOREIGN KEY規格一部分的REFERENCES子句。定義欄位以使用
REFERENCES子句沒有實際效果,而且僅作為提醒或註解,告訴您目前正在定義的欄位是要參考另一個資料表中的欄位。在使用此語法時,務必瞭解:tbl_name(col_name)MySQL 不會執行任何類型的檢查,以確定
col_name實際上是否存在於tbl_name中(甚至tbl_name本身是否存在)。MySQL 不會對
tbl_name執行任何類型的動作,例如在您正在定義的資料表中的列上執行動作時刪除列;換句話說,此語法不會產生任何ON DELETE或ON UPDATE行為。(雖然您可以在REFERENCES子句中撰寫ON DELETE或ON UPDATE子句,但也會被忽略。)此語法會建立一個欄位;它不會建立任何類型的索引或索引鍵。
您可以將如此建立的欄位用作聯結欄位,如下所示:
CREATE TABLE person ( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, name CHAR(60) NOT NULL, PRIMARY KEY (id) ); CREATE TABLE shirt ( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, style ENUM('t-shirt', 'polo', 'dress') NOT NULL, color ENUM('red', 'blue', 'orange', 'white', 'black') NOT NULL, owner SMALLINT UNSIGNED NOT NULL REFERENCES person(id), PRIMARY KEY (id) ); INSERT INTO person VALUES (NULL, 'Antonio Paz'); SELECT @last := LAST_INSERT_ID(); INSERT INTO shirt VALUES ROW(NULL, 'polo', 'blue', @last), ROW(NULL, 'dress', 'white', @last), ROW(NULL, 't-shirt', 'blue', @last); INSERT INTO person VALUES (NULL, 'Lilliana Angelovska'); SELECT @last := LAST_INSERT_ID(); INSERT INTO shirt VALUES ROW(NULL, 'dress', 'orange', @last), ROW(NULL, 'polo', 'red', @last), ROW(NULL, 'dress', 'blue', @last), ROW(NULL, 't-shirt', 'white', @last); SELECT * FROM person; +----+---------------------+ | id | name | +----+---------------------+ | 1 | Antonio Paz | | 2 | Lilliana Angelovska | +----+---------------------+ SELECT * FROM shirt; +----+---------+--------+-------+ | id | style | color | owner | +----+---------+--------+-------+ | 1 | polo | blue | 1 | | 2 | dress | white | 1 | | 3 | t-shirt | blue | 1 | | 4 | dress | orange | 2 | | 5 | polo | red | 2 | | 6 | dress | blue | 2 | | 7 | t-shirt | white | 2 | +----+---------+--------+-------+ SELECT s.* FROM person p INNER JOIN shirt s ON s.owner = p.id WHERE p.name LIKE 'Lilliana%' AND s.color <> 'white'; +----+-------+--------+-------+ | id | style | color | owner | +----+-------+--------+-------+ | 4 | dress | orange | 2 | | 5 | polo | red | 2 | | 6 | dress | blue | 2 | +----+-------+--------+-------+以這種方式使用時,
REFERENCES子句不會顯示在SHOW CREATE TABLE或DESCRIBE的輸出中。mysql> SHOW CREATE TABLE shirt\G *************************** 1. row *************************** Table: shirt Create Table: CREATE TABLE `shirt` ( `id` smallint(5) unsigned NOT NULL auto_increment, `style` enum('t-shirt','polo','dress') NOT NULL, `color` enum('red','blue','orange','white','black') NOT NULL, `owner` smallint(5) unsigned NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
如需外鍵約束的資訊,請參閱 章節 15.1.20.5,「FOREIGN KEY 約束」。