1. 程式人生 > >mysql無法刪除外來鍵

mysql無法刪除外來鍵

1. 什麼是參照完整性?
——————–
參照完整性(完整性約束)是資料庫設計中的一個重要概念,當資料庫中的一個表與一個或多個表進行關聯時都會涉及到參照完整性。比如下面這個例子:
文章分類表 - categories
category_id     name
1               SQL Server
2               Oracle
3               PostgreSQL
5               SQLite

文章表 - articles
article_id      category_id     title
1               1               aa
2               2               bb
3               4               cc

可見以上兩個表之間是通過category_id,其中categories表有4條記錄,articles表有3條記錄。

然而可能因為某種原因我們刪掉了categories 表中category_id=4的記錄,而articles表卻還是有一條category_id=4的記錄,很明顯,category_id=4的這條記錄不應該存在在articles表中,這樣會很容易造成資料錯亂。

相反,外來鍵關係(Foreign Key relationships)討論的是父表(categories)與子表(articles)的關係,通過引入外來鍵(Foreign Key)這個概念來保證參照完整性(Referential integrity),將使會資料庫變的非常簡單。比如,要要做到刪除categories表中category_id=4記錄的同時刪除 articles 表中category_id=4的所有記錄,如果沒有引入外來鍵的話,我們就必須執行2條SQL語句才行;如果有外來鍵的話,可以很容易的用一條SQL語句就可以達到要求。

2. 使用外來鍵的條件
—————–
MySQL只在v3.23.34版本以後才引入外來鍵的,所以在這之前的版本就別想了:),除此之外,還必須具備以下幾個條件:

    1) 在my.cnf配置檔案中開啟InnoDB引擎支援。
    # Uncomment the following if you are using InnoDB tables
    innodb_data_home_dir = /var/db/mysql/
    innodb_data_file_path = ibdata1:10M:autoextend
    innodb_log_group_home_dir = /var/db/mysql/
    innodb_log_arch_dir = /var/db/mysql/

    2) 相關聯的表都必須採用InnoDB引擎。

    3) 相關聯的欄位都必須建立所以。
    MySQL v4.0版本以後,定義外來鍵時會自動建立所以,所以在 v4.0 版本以前(含v4.0版本)必須手工定義索引。

    4) 相關聯的欄位必須採用類似的資料型別,或者說可轉換的資料型別,當然相同型別是最好不過了。
    比如父表的欄位是TINYINT型別,則子表只能採用TINYINT、SMALLINT、INT、BIGINT等幾種型別。

3. 外來鍵語法參考
—————
可以通過 CREATE TABLE 或者 ALTER TABLE 來定義外來鍵。
CREATE TABLE 語法:
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
    [(create_definition,…)]

create_definition:
    column_definition
| [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,…) [reference_definition]

column_definition:
    col_name type [NOT NULL | NULL] [DEFAULT default_value]
        [AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY] KEY]
        [COMMENT ’string’] [reference_definition]

index_col_name:
    col_name [(length)] [ASC | DESC]

reference_definition:
    REFERENCES tbl_name [(index_col_name,…)]
               [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
               [ON DELETE reference_option]
               [ON UPDATE reference_option]

reference_option:
    RESTRICT | CASCADE | SET NULL | NO ACTION

ALTER TABLE 語法:
ALTER [IGNORE] TABLE tbl_name
    alter_specification [, alter_specification] …

alter_specification:
| ADD [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,…)
| ADD [CONSTRAINT [symbol]] UNIQUE [INDEX] [index_name] [index_type] (index_col_name,…)
| ADD [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,…) [reference_definition]
| DROP FOREIGN KEY fk_symbol

4. 定義外來鍵
———–
mysql> CREATE TABLE categories (
    -> category_id tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
    -> name varchar(30) NOT NULL,
    -> PRIMARY KEY(category_id)
    -> ) ENGINE=INNODB;
Query OK, 0 rows affected (0.36 sec)

mysql> INSERT INTO categories VALUES (1, ‘SQL Server’), (2, ‘Oracle’), (3, ‘PostgreSQL’), (4, ‘MySQL’), (5, ‘SQLite’);
Query OK, 5 rows affected (0.48 sec)
Records: 5 Duplicates: 0 Warnings: 0

mysql> CREATE TABLE members (
    -> member_id INT(11) UNSIGNED NOT NULL,
    -> name VARCHAR(20) NOT NULL,
    -> PRIMARY KEY(member_id)
    -> ) ENGINE=INNODB;

Query OK, 0 rows affected (0.55 sec)

mysql> INSERT INTO members VALUES (1, ‘test’), (2, ‘admin’);
Query OK, 2 rows affected (0.44 sec)
Records: 2 Duplicates: 0 Warnings: 0

mysql> CREATE TABLE articles (
    -> article_id INT(11) unsigned NOT NULL AUTO_INCREMENT,
    -> title varchar(255) NOT NULL,
    -> category_id tinyint(3) unsigned NOT NULL,
    -> member_id int(11) unsigned NOT NULL,
    -> INDEX (category_id),
    -> FOREIGN KEY (category_id) REFERENCES categories (category_id),
    -> CONSTRAINT fk_member FOREIGN KEY (member_id) REFERENCES members (member_id),
    -> PRIMARY KEY(article_id)
    -> ) ENGINE=INNODB;

Query OK, 0 rows affected (0.63 sec)

注意:對於非InnoDB表,FOREIGN KEY子句會被忽略掉。
如果遇到如下錯誤:
ERROR 1005: Can’t create table ‘./test/articles.frm’ (errno: 150)
請仔細檢查以下定義語句,常見的錯誤一般都是表型別不是INNODB、相關聯的欄位寫錯了、缺少索引等等。

至此categories.category_id和articles.category_id、members.member_id和 articles.member_id已經建立外來鍵關係,只有 articles.category_id 的值存在與 categories.category_id 表中並且articles.member_id的值存在與members.member_id表中才會允許被插入或修改。例如:

mysql> INSERT INTO articles (category_id, member_id, title) VALUES (6, 1, ‘foo’);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test/articles`, CONSTRAINT `articles_ibfk_1` FOREIGN KEY (`category_id`)REFERENCES `categories` (`id`))

mysql> INSERT INTO articles (category_id, member_id, title) VALUES (3, 3, ‘foo’);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test/articles`, CONSTRAINT `fk_member` FOREIGN KEY (`member_id`) REFERENCES `members` (`member_id`))

可見上面兩條語句都會出現錯誤,因為在categories表中並沒有category_id=6、members表中也沒有member_id=3的記錄,所以不能插入。而下面這條SQL語句就可以。

mysql> INSERT INTO articles (category_id, member_id, title) VALUES (3, 2, ‘bar’);
Query OK, 1 row affected (0.03 sec)

5. 刪除外來鍵定義
—————
不知道大家有沒有發現,在前面定義外來鍵的時候articles.member_id外來鍵比articles.category_id子句多了一個CONSTRAINT fk_member ?
這個fk_member就是用來刪除外來鍵定義用的,如下所示:
mysql> ALTER TABLE articles DROP FOREIGN KEY fk_member;
Query OK, 1 row affected (0.25 sec)
Records: 1 Duplicates: 0 Warnings: 0

然後在刪除:ALTER TABLE FEED DROP FEED_SOURCE_ID;這樣就把這個欄位刪除了!

這樣articles.member_id外來鍵定義就被刪除了,但是如果定義時沒有指定CONSTRAINT fk_symbol (即外來鍵符號)時該怎麼刪除呢?別急,沒有指定時,MySQL會自己建立一個,可以通過以下命令檢視:

mysql> SHOW CREATE TABLE articles;
+———-+————————————+
| Table    | Create Table                       |
+———-+————————————+
| articles | CREATE TABLE `articles` (
`article_id` int(11) unsigned NOT NULL auto_increment,
`category_id` tinyint(3) unsigned NOT NULL,
`member_id` int(11) unsigned NOT NULL,
`title` varchar(255) NOT NULL,
PRIMARY KEY (`article_id`),
KEY `category_id` (`category_id`),
KEY `member_id` (`member_id`),
CONSTRAINT `articles_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1          |
+———-+————————————+
1 row in set (0.01 sec)

可以看出articles.category_id的外來鍵符號為articles_ibfk_1,因為就可以執行以下命令刪除外來鍵定義:

mysql> ALTER TABLE articles DROP FOREIGN KEY articles_ibfk_1;
Query OK, 1 row affected (0.66 sec)
Records: 1 Duplicates: 0 Warnings: 0

6. 總結
——-
引入外來鍵的缺點是會使速度和效能下降,當然外來鍵所帶來的優點還有很多,本文僅討論如何定義、刪除外來鍵。至於外來鍵的實際應用將會在以後的文章中介紹。