1. 程式人生 > >資料庫設計(2)主鍵、外來鍵

資料庫設計(2)主鍵、外來鍵


轉自:http://blog.csdn.net/championhengyi/article/details/78559789

什麼是主鍵、外來鍵

關係型資料庫中的一條記錄中有若干個屬性,若其中某一個屬性組(注意是組)能唯一標識一條記錄,該屬性組就可以成為一個主鍵。

比如:

學生表(學號,姓名,性別,班級) 
其中每個學生的學號是唯一的,學號就是一個主鍵

課程表(課程編號,課程名,學分) 
其中課程編號是唯一的,課程編號就是一個主鍵

成績表(學號,課程號,成績) 
成績表中單一一個屬性無法唯一標識一條記錄,學號和課程號的組合才可以唯一標識一條記錄,所以學號和課程號的屬性組是一個主鍵

成績表中的學號不是成績表的主鍵,但它和學生表中的學號相對應,並且學生表中的學號是學生表的主鍵,則稱成績表中的學號是學生表的外來鍵。

同理:成績表中的課程號是課程表的外來鍵。

定義主鍵和外來鍵主要是為了維護關係資料庫的完整性,總結一下:

1.主鍵是能確定一條記錄的唯一標識,比如,一條記錄包括身份正號,姓名,年齡。身份證號是唯一能確定你這個人的,其他都可能有重複,所以,身份證號是主鍵。

2.外來鍵用於與另一張表的關聯。是能確定另一張表記錄的欄位,用於保持資料的一致性。比如,A表中的一個欄位,是B表的主鍵,那他就可以是A表的外來鍵。


主鍵、外來鍵和索引的區別

  主鍵 外來鍵 索引
定義 唯一標識一條記錄,不能有重複的,不允許為NULL 表的外來鍵是另一表的主鍵, 外來鍵可以有重複的, 可以是NULL 沒有重複值,可以為NULL(會使索引無效)
作用 用來保證資料完整性 用來和其他表建立聯絡用的 提高查詢排序的速度
個數 主鍵只能有一個 一個表可以有多個外來鍵 一個表可以有多個惟一索引

外來鍵約束

在上面“什麼是主鍵、外來鍵” 一小節中,我給大家灌輸的思維是,學生表使用學號作為主鍵,課程表使用課程ID作為主鍵,成績表使用學號、課程ID作為聯合主鍵(聯合主鍵(使用組合索引進行替代)以後壓根就別用,主鍵的設計原則就是欄位數目越少越好),這樣就產成了一個問題,外來鍵的參考鍵必須是另一個表的主鍵嗎?

答案當然不是,但是參考鍵必須是唯一性索引。主鍵約束和唯一性約束都是唯一性索引。


錯誤的設計方式—[1215] Cannot add foreign key constraint

出現這種問題的原因一般有兩個:

1.兩張表裡要設主鍵和外來鍵的欄位的資料型別或者資料長度不一樣。 
2.某個表裡已經有記錄了。

我當時屬於第一個。


如何設計良好的資料庫主鍵

摘抄一位知乎使用者的回答:知乎連結—紀路

主鍵的話我的建議是自增整形,不要使用與業務相關的名字,僅用id即可,而效率問題都可以用索引來解決。因為主鍵的不可變的特性,如果選擇不慎,會在未來產生難以預期的問題。比如你用int型做文章的id,但是如果在未來某一天文章數超過了無符號整形的最大值,你將沒法將主鍵修改成bigint。或者為了給使用者起一個唯一id用了自增主鍵,但是如果未來有其他的專案使用者要合併進來,他也是這麼做的。這時候為了區分不同的專案可能要在這個使用者id前加一個字首,這時候也沒法修改主鍵的值。主鍵之所以叫做主鍵就是到什麼時候都不能改,所以最好的方案就是使用自增數字id做主鍵,並且不要給這個主鍵賦予一個業務相關的意義。

總結上面前輩的一句話就是,不要將表中與業務相關的欄位設定為主鍵,即使它可以唯一標識這一行,比如身份證號,學號等等,主鍵越沒有意義,說明主鍵設定的越好


主鍵、外來鍵的使用

建立表

就按照我們上面的例子來建立三張表吧:student、course、score表。

建立student表:


   
  1. create table student
  2. (
  3. pk_id bigint unsigned not null auto_increment primary key,
  4. uk_sno int(10) unsigned not null,
  5. name char(60) not null,
  6. sex char(10) not null,
  7. class char(60) not null,
  8. constraint uk_sno unique (sno)
  9. )enige = InnoDB, charset = utf8
  10. ;

建立course表:


   
  1. create table course
  2. (
  3. pk_id bigint unsigned not null auto_increment primary key,
  4. uk_course_id int(10) unsigned not null,
  5. course_name char(30) not null,
  6. credit int not null,
  7. constraint uk_course_id unique (course_id)
  8. )enige = InnoDB, charset=utf8
  9. ;

建立score表:


   
  1. create table score
  2. (
  3. pk_id bigint not null auto_increment primary key,
  4. fk_sno int(10) unsigned not null,
  5. fk_course_id int(10) unsigned not null,
  6. result int not null,
  7. constraint fk_sno foreign key (fk_sno) references <databasename>.student (sno),
  8. constraint fk_course_id foreign key (fk_course_id) references <databasename>.course (course_id)
  9. )enige = InnoDB, charset=utf8
  10. ;

值得一說的是,建立外來鍵的時候也會自動建立普通索引,所以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表):


   
  1. create table score
  2. (
  3. pk_id bigint not null auto_increment primary key,
  4. fk_sno int(10) unsigned not null,
  5. fk_course_id int(10) unsigned not null,
  6. result int not null,
  7. constraint fk_sno foreign key (fk_sno) references <databasename>.student (sno) on update cascade on delete cascade,
  8. constraint fk_course_id foreign key (fk_course_id) references <databasename>.course (course_id) on update cascade on delete cascade
  9. )enige = InnoDB, charset=utf8
  10. ;