MySQL主鍵約束和外鍵約束詳解
1、PRIMARY KEY
上期我們講述了 not null 和 unique 約束,而本期的第一個約束就是這倆的結合體,也成為主鍵約束。
主鍵約束,說白了就是 not null + unique,主鍵也是在插入數據的時候先進行查詢,而 MySQL 則會對 unique,primary key 這樣的列自動的添加索引(后續(xù)介紹),來提高查詢的效率。
● 在實際開發(fā)中,大部分的表,一般都會帶有一個主鍵,主鍵往往是一個整數表示的 id。
create table student (
id int primary key,
name varchar(10)
);只要你給改列設置了主鍵,意味著后續(xù)插入的數據,既不能重復,也不能為空!
● 在 MySQL 中,一個表中只能有一個主鍵,不能有多個。
create table student (
id int primary key,
name varchar(10) primary ke
);
-- ERROR 1068 (42000): Multiple primary key defined● 雖然主鍵不能有多個,但支持將多個列放到一起,共同作為主鍵,稱為聯合主鍵。
create table student (
id int,
name varchar(10),
primary key (id, name)
);desc student; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(10) | NO | PRI | NULL | | +-------+-------------+------+-----+---------+-------+ -- 2 rows in set (0.00 sec)
這樣我們就能發(fā)現 Key 這一列有兩個 PRI 了,PRI 是主鍵的簡寫,表示是主鍵字段,同時也能看到 Null 這一列為 NO,所以主鍵包含了 not null 和 unique 的特性。
● 主鍵還有一個非常常用的用法,使用 MySQL 自帶的自增主鍵作為主鍵的值。
create table student (
id int primary key auto_increment,
name varchar(10)
);這里我們就來插入數據試一試:
insert into student value
(88, '李四'),
(12, '張三');這里仍然可以插入成功,由此能發(fā)現設置了自增主鍵仍然能自定義值,那么下面如果不自定義 id 的值呢?會自動生成多少呢?
insert into student(name) values('王五');
-- Query OK, 1 row affected (0.00 sec)
select * from student;
+----+--------+
| id | name |
+----+--------+
| 12 | 張三 |
| 88 | 李四 |
| 89 | 王五 |
+----+--------+
-- 3 rows in set (0.00 sec)注意:這里可以看到,我們插入是 李四,張三,但是查詢卻是 張三在前李四在后,所以 MySQL 并未保證插入順序和取出是順序是一致的!
再者這里可以發(fā)現,自增主鍵插入數據對應字段不給值時,使用最大值+1。那如果我把王五刪了,再次插入一條數據,自增主鍵會是多少呢?
delete from student where name = '王五';
-- Query OK, 1 row affected (0.00 sec)
insert into student(name) values('趙六');
-- Query OK, 1 row affected (0.00 sec)
select * from student;
+----+--------+
| id | name |
+----+--------+
| 12 | 張三 |
| 88 | 李四 |
| 90 | 趙六 |
+----+--------+
-- 3 rows in set (0.00 sec)看到沒!并不是按照當前列的最大值來自增主鍵的,你可以理解為 MySQL 有個記錄主鍵最大值的玩意,是按照這個之前主鍵最大值自增的!刪除了數據,不影響主鍵的最大值!
我們再來看一個操作:
insert into student values(null, '孫七');
這個操作并不是插入 null 數據,而是交給數據庫使用自增主鍵!
這里又有一個疑問了,如果數據量太大,一個服務器存不下那么多數據怎么辦?那就要采用分庫分表的操作了,多臺服務器來存儲了,本質上就是把一張大表,分成多張小表,每個服務器分別只存一部分大大數據,這就可以理解成分布式處理了,
問題來了,上述這樣的情況,還能使用自增主鍵嗎?
這里涉及到一個 "分布式系統(tǒng)中唯一 id 生成算法" ,也就是全局唯一 id,常見的即:
實現公式:時間戳+主機編號+隨機因子 => 全局唯一的 id。
當然感興趣的可以下來進一步了解下,這里就不過多闡述了。
2、FOREIGN KEY
外鍵約束,說白了就是兩個表之間相互約束。
注意:外鍵是用于關聯其他表的主鍵或唯一鍵!
foreign key (字段名) references 主表(列)
這里我們舉個例子,在學校里,有很多個班級,每個班級里有很多學生,也就是一個學生對應一個班級,一個班級對應多個學生,假設某某學校,只有三個 java 班級,分別是 java1班,java2班,java3班,每個班級又有若干個學生,現在開學了,學生去學校報道, 報道的時候呢會登記你的班級,志愿者一問,同學你哪個班的呀?我說:我 java2 班的,于是順利登記成功,后面又來了一個同學,同樣的問題,同學你哪個班的呀?我 java6班,志愿者一查詢,咱學校沒有 java6班呀,于是那個同學就被老師帶走調查了...
上述只是一個故事,可以把我們自己想象成一條數據,當然登記信息肯定不僅僅是登記班級,還有有 學號 姓名 性別 身份證 班級號... 而這里我們的班級號不是隨便哪個都可以的,是有固定的幾個班級號,如果這幾個班級號中沒有你報的班級號,那么你的信息,就錄不上去,也就是一個錯誤的信息。假設這幾個班級號放在一起,對應一張班級表,咱們登記登記的信息,對應學生表, 那么就是兩張表之間指定字段的約束。
有了上述鋪墊,我們就來用代碼來理解一下:
● 首先創(chuàng)建一張班級表 classes,id 為主鍵:
create table classes (
id int primary key,
name varchar(10)
);這樣 classes 班級表就建好了,這里有個問題,為什么 id 要作為主鍵呢?
答:首先這里的 id 對應班級號,name 是班級名稱,而外鍵是用于關聯其他表的主鍵或唯一鍵,所以說這里的 id 字段,要不是主鍵,要不是唯一鍵。
未來學生表會有一個字段擁有外鍵屬性,來關聯這個 classes 的 id 字段?。。?/p>
● 創(chuàng)建學生表 student 指定 classId 為外鍵,關聯 classes 表中 id 字段
create table student (
id int,
name varchar(10),
sex varchar(1),
classId int,
foreign key (classId) references classes(id)
);這段 sql 語句最后一條,foreign key (classId) references classes(id),這條語句表示,將創(chuàng)建的 student 表中 classId 字段作為外鍵,于 classes 表中 id 字段關聯!
這兩張表是創(chuàng)建好了,現在得創(chuàng)建數據了,為 classes 表中創(chuàng)建三個班級數據:
insert into classes value
(1, 'java1班'),
(2, 'java2班'),
(3, 'java3班');這樣就能表示有存在了三個班級,分別是 1班,2班,3班,如果接下來我去登記信息,錄入的班級在這三個班之中,那就毫無問題:
insert into student values (230115, '籃球哥', '男', 2); -- Query OK, 1 row affected (0.00 sec)
這里是不是就如同我們上面舉例說明的這樣,我是 java2 班的,而 classId 錄入了一個 2,關聯 classes 的 id 字段 2,對應的就是 java2 班。
那如果后來的同學,錄入一個 java6 班呢?
insert into student values (230205, '小王', '男', 6); -- ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`demo`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`classId`) REFERENCES `classes` (`id`))
那么此處的外鍵約束的含義,要求了 student 里的 classId 務必要在 classes 表的 id 列中存在!
相信看到這,大家都能明白外鍵約束的作用了,student 表中的數據,要依賴 classes 表的數據,而 classes 表的數據要對 student 表產生約束力,就像 父親 對 孩子 有約束力。此處起到約束作用的表我們稱為 "父表",被約束的表我們稱為 "子表"。
看到這里啊,說白了就是 父表 對 子表 起到了約束作用,限制了約束的字段的取值.
這里我不禁想到一句話,當你在凝視深淵的時候,深淵也在凝視你!
我們表面上看確實是 父表 對 子表 起到約束,但反過來其實 子表 也限制著 父表!
這里我們嘗試刪除父表中 id 為 2 的數據,也就是刪除 java1班:
delete from classes where id = 2; -- ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`demo`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`classId`) REFERENCES `classes` (`id`))
發(fā)現報錯了,其實我們想也能想到,如果真能刪除,那子表中 classesId = 2 的那一條數據不就尷尬了嗎?那這外鍵約束建了又像沒有建一樣,所以就僵住了,顯然是不允許這樣的情況發(fā)生的!
當然 classes 表中 id 為 1 的數據可以刪除,因為 子表 并不存在 classId 為 1 的數據嘛:
delete from classes where id = 1; -- Query OK, 1 row affected (0.00 sec)
問題:可以先建子表再建父表嗎?
顯然是不行的呀,當你建子表指定外鍵約束的時候,你父表都沒有,怎么建約束?
3、表的設計
表的設計算是一個比較抽象的概念,有了一定經驗后,會更好理解,結合后期的項目設計,再來理解表設計,可能會更好,這里就簡單介紹下常見設計。
3.1 一對一
比如說我們每個人都有的身份證,每個身份證id,就對應一個人,這就是一對一關系。
這種一對一關系可以如何創(chuàng)建表呢?
● 第一種方案
create table person (
id int primary key,
name varchar(10),
address varchar(50),
sex varchar(1)
);把身份證id和姓名直接放在一張表里.
● 第二種方案
create table person (
id int primary key,
name varchar(10)
);
create table account (
accountId int primary key,
personName varchar(10),
address varchar(50),
sex varchar(1),
id int,
foreign key (id) references person(id)
);第二種方案則是身份證號和姓名放在不同的表里,相互關聯。
3.2 一對多
這個例子很像我們之前說過的,一個班級有多個學生,一個學生對應一個班級,這里就不在贅述。
3.3 多對多
比如說學校的一門課程,可以被多名學生選修,而一名學生可以選修多門課程,這就是多對多的關系:

這里我們只需要讓 student 表 studentId 字段 約束 student_course 表的 studentId 字段,以及 course 表 courseId 字段約束 student_course 表的 courseId 字段即可。
到此這篇關于MySQL主鍵約束和外鍵約束的文章就介紹到這了,更多相關mysql主鍵約束和外鍵約束內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
區(qū)分MySQL中的空值(null)和空字符('''')
這篇文章主要介紹了如何區(qū)分MySQL中的空值(null)和空字符(''),幫助大家更好的理解和使用MySQL數據庫,感興趣的朋友可以了解下2020-09-09
淺談MySQL存儲過程中declare和set定義變量的區(qū)別
下面小編就為大家?guī)硪黄獪\談MySQL存儲過程中declare和set定義變量的區(qū)別。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-12-12

