數據庫設計中一對一、多對一、多對多關系依據外鍵的實現條件及方法


作者:二歪求知iSk2y
鏈接:https://www.jianshu.com/p/2b27c7ba0653
來源:簡書

下面以departments和staff_info表為例(為staff_info添加指向departments的外鍵)

一個表的字段作為外鍵的條件:

列值必須非空且唯一

測試例子如下:
mysql> create table departments (dep_id int(4),dep_name varchar(11));
Query OK, 0 rows affected (0.02 sec)

mysql> desc departments;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| dep_id   | int(4)      | YES  |     | NULL    |       |
| dep_name | varchar(11) | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+
rows in set (0.00 sec)

# 創建外鍵不成功
mysql> create table staff_info (s_id int,name varchar(20),dep_id int,foreign key(dep_id) references departments(dep_id));
ERROR 1215 (HY000): Cannot add foreign key 

# 設置dep_id非空,仍然不能成功創建外鍵
mysql> alter table departments modify dep_id int(4) not null;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc departments;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| dep_id   | int(4)      | NO   |     | NULL    |       |
| dep_name | varchar(11) | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+
rows in set (0.00 sec)

mysql> create table staff_info (s_id int,name varchar(20),dep_id int,foreign key(dep_id) references departments(dep_id));
ERROR 1215 (HY000): Cannot add foreign key constraint

# 當設置字段為unique唯一字段時,設置該字段為外鍵成功
mysql> alter table departments modify dep_id int(4) unique;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc departments;                                                              
+----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+---------+-------+ | dep_id | int(4) | YES | UNI | NULL | | | dep_name | varchar(11) | YES | | NULL | | +----------+-------------+------+-----+---------+-------+ rows in set (0.01 sec) mysql> create table staff_info (s_id int,name varchar(20),dep_id int,foreign key(dep_id) references departments(dep_id)); Query OK, 0 rows affected (0.02 sec)

特別注意:需要主外結合的兩個表必須是使用同樣的一個引擎類型:兩個表必須都是innodb存儲引擎

 

 

添加外鍵的方法

 

一般有兩種方法,在創建表的時候添加,或者后期再添加

 

創建時添加

mysql> create table score(
    -> sid int not null auto_increment primary key,
    -> student_id int,
    -> corse_id int,
    -> number int not null,
    -> constraint fk_sid foreign key (student_id) references student(sid),
    -> constraint fk_corse_id foreign key (corse_id) references course(cid));
 
-----------------------------------------------------------------------------------
[CONSTRAINT symbol] FOREIGN KEY [id] (從表的字段1)

REFERENCES tbl_name (主表的字段2)

[ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION}]

[ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION} 

CONSTRAINT symbol:可以給這個外鍵約束起一個名字,有了名字,以后找到它就很方便了。如果不加此參數的話,系統會自動分配一個名字。

FOREIGN KEY:將從表中的字段1作為外鍵的字段。

REFERENCES:映射到主表的字段2。

ON DELETE后面的四個參數:代表的是當刪除主表的記錄時,所做的約定。

創建后添加

ALTER TABLE employee ADD FOREIGN KEY(dept_id) REFERENCES department(id); 

ALTER TABLE employee:在從表employee中進行操作;

ADD FOREIGN KEY(dept_id):將從表的字段dept_id添加為外鍵;

REFERENCES department(id):映射到主表department當中為id的字段。

刪除外鍵

刪除外鍵時如果你不知道外鍵名字 就先獲取外鍵名字

show create table emp \G;

刪除外鍵

alter table emp drop foreign key 外鍵名; 

 

下面的才是重點

關系的實現

在實體關系模型中,我們知道有三種關系:一對一、一對多、多對多。這只是概念上的關系,但是在真實的關系數據庫中,我們只有外鍵,並沒有這三種關系,那么我們就來說一說在關系數據庫管理系統中,怎么實現這三種關系。

一對多

這里先講解一對多,因為這個關系最簡單。一對多和多對一是一回事,所以就不再提多對一這個詞。一對多的概念是一個對象A會對應多個對象B,而從B的角度看,一個對象B只會對於一個對象A。比如說班級和學生就是一對多關系。一個班級對應多個學生,一個學生只會對於一個班級。

​ 一對多的關系之所以說簡單,是因為RDBMS的【外鍵】其實就是表示一對多關系。對於一對多關系,我們只需要在“多”的這個表中建立“一”的外鍵關聯即可,而“一”這邊的表不需要做任何修改。比如前面說到的班級學生關系。班級表不變,學生表增加班級Id作為外鍵。

多對多

多對多的關系在數據庫設計時比一對一要常見,所以這里先說說多對多。多對多是一個對象A對應多個對象B,從B角度看,一個對象B也會對應多個對象A。比如說學生和課程的關系就是多對多關系。一個學生會學習多門課程,一門課程會有多個學生來選修。

​ 在RDBMS中,必須使用中間表來表示多對多的關系。中間表我們可以分成兩種,一種是純粹表示關系的中間表,一種是表示中間實體的中間表。

​ 純粹表示關系的中間表很簡單,只需要兩列:AID和BID,AID以外鍵關聯到A表的主鍵,BID以外鍵關聯到B表的主鍵,然后這兩個列組成聯合主鍵。這個中間表純粹是表示多對多關系而存在,在業務上不會有對應的實體與之對應。比如前面提到的學生和課程的關系,如果我們只需要知道哪些學生上哪些課,哪些課有哪些學生選,不需要有更多的信息的情況下,我們就可以建立“學生課程”中間表,里面只有學生ID和課程ID兩個字段。

​ 中間實體是在純粹的中間關系表的基礎上,加上了更多的屬性,從而形成了一個新的實體。比如前面提到的學生和課程的關系,如果我們需要記錄學生選課的時間、學生選擇這門課程后的考試成績,那么我們就像建立一個“選課”實體,該實體具有如下屬性:

 

​ 這就是一個中間實體,已經完全脫離了普通的多對多關系中間表,而變成一個實體的形式的存在,所以按照前面博客中講到的主鍵設計的原則,我們可以單獨建立一個選課ID的列作為數據庫的主鍵,該主鍵本身並沒有業務含義。

一對一

一對一概念上是說一個對象A最多對應一個對象B,從B角度看,也是一個對象B最多對應一個對象A。比如說班主任(教師)和班級的關系,一個班主任最多管理一個班級,一個班級也最多只有一個班主任。

​ 一對一的關系在數據庫設計中,是使用的最少的關系,因為一般來說,如果兩個實體是一對多關系,那么我們也可以把這兩個實體合並成一個實體。但是在設計中,我們仍然會遇到兩個完全不同的實體,之間存在一對一關系。

​ 一對一的RDBMS實現是在其中的一個表上建立外鍵指向另一個表,同時在該外鍵列上建立唯一約束。比如前面說到的班主任和班級關系,我們可以在班級表建立班主任字段,然后再在該字段建立唯一約束。因為每個班都會有班主任,所以班主任字段是不允許為空的。一個教師可以當某個班的班主任,也可以不當任和班的班主任,同時也不可能在班級表的班主任字段上出現兩次,所以最多就當一個班的班主任,所以該設計滿足需求。

​ 那么我們可不可以反過來,在教師表中建立所管理的班級Id字段,指向班級表,並建立唯一約束呢?除了不滿足“每個班必然有一個班主任”這個業務約束外,其他都沒有問題。所以如果對於一對一的情況,如果那邊必須要求持有另一邊,則就在哪邊增加外鍵字段;如果沒有要求必須持有一個另一類實體的話,就哪邊添加外鍵列都行。


/**學生表*/
CREATE TABLE student (
stu_id INT AUTO_INCREMENT,
NAME VARCHAR(30),
age INT ,
class VARCHAR(50),
address VARCHAR(100),
PRIMARY KEY(stu_id)
)
 
/*學生課程表*/
CREATE TABLE Course(
cour_id INT AUTO_INCREMENT,
NAME VARCHAR(50),
CODE VARCHAR(30),
PRIMARY KEY(cour_id)
)
 
/**學生課程關聯表*/
CREATE TABLE Stu_Cour(
sc_id INT AUTO_INCREMENT,
stu_id INT ,
cour_id INT,
PRIMARY KEY(sc_id)
)

/*添加外鍵約束*/
ALTER TABLE Stu_Cour ADD CONSTRAINT stu_FK1 FOREIGN KEY(stu_id) REFERENCES student(stu_id)
ALTER TABLE Stu_Cour ADD CONSTRAINT cour_FK2 FOREIGN KEY(cour_id) REFERENCES Course(cour_id)

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM