什麼是主鍵、外鍵
關係型數據庫中的一條記錄中有若干個屬性,若其中某一個屬性組(注意是組)能惟一標識一條記錄,該屬性組就能夠成爲一個主鍵。數據庫
好比:併發
學生表(學號,姓名,性別,班級)
其中每一個學生的學號是惟一的,學號就是一個主鍵app
課程表(課程編號,課程名,學分)
其中課程編號是惟一的,課程編號就是一個主鍵分佈式
成績表(學號,課程號,成績)
成績表中單一一個屬性沒法惟一標識一條記錄,學號和課程號的組合才能夠惟一標識一條記錄,因此學號和課程號的屬性組是一個主鍵ide
成績表中的學號不是成績表的主鍵,但它和學生表中的學號相對應,而且學生表中的學號是學生表的主鍵,則稱成績表中的學號是學生表的外鍵。高併發
同理:成績表中的課程號是課程表的外鍵。性能
定義主鍵和外鍵主要是爲了維護關係數據庫的完整性,總結一下:學習
1.主鍵是能肯定一條記錄的惟一標識,好比,一條記錄包括身份正號,姓名,年齡。身份證號是惟一能肯定你這我的的,其餘均可能有重複,因此,身份證號是主鍵。spa
2.外鍵用於與另外一張表的關聯。是能肯定另外一張表記錄的字段,用於保持數據的一致性。好比,A表中的一個字段,是B表的主鍵,那他就能夠是A表的外鍵。.net
主鍵、外鍵和索引的區別
主鍵 外鍵 索引
定義 惟一標識一條記錄,不能有重複的,不容許爲NULL 表的外鍵是另外一表的主鍵, 外鍵能夠有重複的, 能夠是NULL 沒有重複值,能夠爲NULL(會使索引無效)
做用 用來保證數據完整性 用來和其餘表創建聯繫用的 提升查詢排序的速度
個數 主鍵只能有一個 一個表能夠有多個外鍵 一個表能夠有多個唯一索引
外鍵約束
在上面「什麼是主鍵、外鍵」 一小節中,我給你們灌輸的思惟是,學生表使用學號做爲主鍵,課程表使用課程ID做爲主鍵,成績表使用學號、課程ID做爲聯合主鍵(聯合主鍵(使用組合索引進行替代)之後壓根就別用,主鍵的設計原則就是字段數目越少越好),這樣就產成了一個問題,外鍵的參考鍵必須是另外一個表的主鍵嗎?
答案固然不是,可是參考鍵必須是惟一性索引。主鍵約束和惟一性約束都是惟一性索引。
錯誤的設計方式—[1215] Cannot add foreign key constraint
出現這種問題的緣由通常有兩個:
1.兩張表裏要設主鍵和外鍵的字段的數據類型或者數據長度不同。
2.某個表裏已經有記錄了。
我當時屬於第一個。
如何設計良好的數據庫主鍵
摘抄一位知乎用戶的回答:知乎連接—紀路
主鍵的話個人建議是自增整形,不要使用與業務相關的名字,僅用id便可,而效率問題均可以用索引來解決。由於主鍵的不可變的特性,若是選擇不慎,會在將來產生難以預期的問題。好比你用int型作文章的id,可是若是在將來某一天文章數超過了無符號整形的最大值,你將無法將主鍵修改爲bigint。或者爲了給用戶起一個惟一id用了自增主鍵,可是若是將來有其餘的項目用戶要合併進來,他也是這麼作的。這時候爲了區分不一樣的項目可能要在這個用戶id前加一個前綴,這時候也無法修改主鍵的值。主鍵之因此叫作主鍵就是到何時都不能改,因此最好的方案就是使用自增數字id作主鍵,而且不要給這個主鍵賦予一個業務相關的意義。
總結上面前輩的一句話就是,不要將表中與業務相關的字段設置爲主鍵,即便它能夠惟一標識這一行,好比身份證號,學號等等,主鍵越沒有意義,說明主鍵設置的越好。
主鍵、外鍵的使用
建立表
就按照咱們上面的例子來創建三張表吧:student、course、score表。
建立student表:
create table student( pk_id bigint unsigned not null auto_increment primary key, uk_sno int(10) unsigned not null, name char(60) not null, sex char(10) not null, class char(60) not null, constraint uk_sno unique (sno) )enige = InnoDB, charset = utf8;
建立course表:
create table course( pk_id bigint unsigned not null auto_increment primary key, uk_course_id int(10) unsigned not null, course_name char(30) not null, credit int not null, constraint uk_course_id unique (course_id) )enige = InnoDB, charset=utf8;
建立score表:
create table score( pk_id bigint not null auto_increment primary key, fk_sno int(10) unsigned not null, fk_course_id int(10) unsigned not null, result int not null, constraint fk_sno foreign key (fk_sno) references <databasename>.student (sno), constraint fk_course_id foreign key (fk_course_id) references <databasename>.course (course_id) )enige = InnoDB, charset=utf8;
值得一說的是,建立外鍵的時候也會自動建立普通索引,因此fk_sno、fk_course_id實際上是兩個普通索引的名稱。
對於使用IDEA的同窗,咱們會發如今設置外鍵的時候還有Update rule 和 Delete rule規則,對於這兩個選項的解釋,咱們下面再說。
外鍵的使用–更新與刪除
表已經創建成功,如今咱們插入數據:
student表:
INSERT INTO student(uk_sno, name, sex, class) VALUES(123456, "spider_hgyi", "male", "cs");
crouse表:
INSERT INTO course(uk_course_id, course_name, credit) VALUES(1, "csapp", 10);
score表:
INSERT INTO score(fk_sno, fk_course_id, result) VALUES(123456, 1, 100);
好了,如今三個表裏都已經有了數據,如今咱們嘗試更新學生表中學號的信息:
UPDATE student SET uk_sno=12345678 WHERE uk_sno=123456;
MySQL報錯:
(1451, 'Cannot delete or update a parent row: a foreign key constraint fails (`bookmanager`.`score`, CONSTRAINT `fk_sno` FOREIGN KEY (`fk_sno`) REFERENCES `student` (`uk_sno`))')
看看錯誤告訴咱們什麼:不能刪除或更新這一行,存在外鍵約束,score表中的fk_sno列是當前要更新的uk_sno的外鍵,也就是說,你要更新學生表中的學號,可是成績表中的學號是你的外鍵,你不能無論它呀,刪除也是同理。
要怎麼解決?
還記得剛纔我貼的那張IDEA的圖片嗎?那兩個規則就能夠幫助咱們解決這個問題。
級聯刪除與更新
咱們在更新與刪除時遇到的外鍵約束解決方案分別對應設置Update rule與Delete rule。有以下四個選項:
1.CASCADE:從父表刪除或更新且自動刪除或更新子表中匹配的行。
2.SET NULL:從父表刪除或更新行,並設置子表中的外鍵列爲NULL。若是使用該選項,必須保證子表列沒有指定NOT NULL。
3.RESTRICT:拒絕對父表的刪除或更新操做。
4.NO ACTION:標準SQL的關鍵字,在MySQL中與RESTRICT相同。
能夠看到我在建立外鍵的時候選擇的是NO ACTION,也就是第四個選項。咱們只須要選擇CASCADE就能夠啦。具體效果就不進行演示了。
若是你不用IDEA也不要緊,接下來我給出SQL語句的實現(從新建立score表):
create table score( pk_id bigint not null auto_increment primary key, fk_sno int(10) unsigned not null, fk_course_id int(10) unsigned not null, result int not null, constraint fk_sno foreign key (fk_sno) references <databasename>.student (sno) on update cascade on delete cascade, constraint fk_course_id foreign key (fk_course_id) references <databasename>.course (course_id) on update cascade on delete cascade )enige = InnoDB, charset=utf8;
補充
博主在學習阿里的Java開發手冊時,他們對於外鍵與級聯是這樣描述的:
【強制】不得使用外鍵與級聯,一切外鍵概念必須在應用層解決。
說明:以學生和成績的關係爲例,學生表中的 student _ id 是主鍵,那麼成績表中的 student _ id則爲外鍵。若是更新學生表中的 student _ id ,同時觸發成績表中的 student _ id 更新,即爲級聯更新。外鍵與級聯更新適用於單機低併發,不適合分佈式、高併發集羣 ; 級聯更新是強阻塞,存在數據庫更新風暴的風險;外鍵影響數據庫的插入速度。
原本我是打算之後在腦海中拋棄外鍵與級聯這部分知識的,但通過學長的敲打,不得不說我對阿里的盲目崇拜。
外鍵約束、級聯更新與刪除對於開發者是很是有用的,它確保了數據刪除與更新的完整性。至於阿里所說的影響性能,學長反問我:「你的應用有多少人在用?阿里的應用有多少人在用?」。
說這這些話的緣由也是此次提醒我在軟件開發的過程當中應想好受衆的大小,靈活運用所學的知識,不能盲目追求課本以及參考資料。
參考資料
外鍵必須是另外一個表的主鍵嗎
關於數據庫主鍵和外鍵(終於弄懂啦)
快速理解MySQL中主鍵與外鍵的實例教程
MySQL使用外鍵實現級聯刪除與更新的方法
阿里巴巴Java開發手冊–MySQL數據庫
https://blog.csdn.net/championhengyi/article/details/78559789