MySQL 表約束從基礎(chǔ)約束到外鍵關(guān)聯(lián)實(shí)戰(zhàn)案例詳解
前言:
在 MySQL 數(shù)據(jù)庫(kù)設(shè)計(jì)中,數(shù)據(jù)類型定義了字段的存儲(chǔ)格式,而表約束則從業(yè)務(wù)邏輯層面保證數(shù)據(jù)的合法性和完整性。沒(méi)有約束的表可能出現(xiàn)空值、重復(fù)數(shù)據(jù)、邏輯沖突等問(wèn)題(如學(xué)生所屬班級(jí)不存在),而合理使用約束能讓數(shù)據(jù)庫(kù) “自我校驗(yàn)”,減少程序中的數(shù)據(jù)校驗(yàn)邏輯。本文將全面拆解 MySQL 核心表約束,結(jié)合 PPT 實(shí)戰(zhàn)案例講解用法、區(qū)別與避坑點(diǎn),幫你設(shè)計(jì)出健壯的數(shù)據(jù)庫(kù)表結(jié)構(gòu)。
一. 表約束核心概念
表約束是對(duì)表中字段的規(guī)則限制,用于保證數(shù)據(jù)的準(zhǔn)確性、唯一性和關(guān)聯(lián)性。MySQL 支持的核心約束包括:
- 空屬性約束(
NULL/NOT NULL):限制字段是否允許為空; - 默認(rèn)值約束(
DEFAULT):字段未賦值時(shí)自動(dòng)使用默認(rèn)值; - 列描述(
COMMENT):字段說(shuō)明(無(wú)校驗(yàn)作用); - 零填充約束(
ZEROFILL):數(shù)字類型不足指定寬度時(shí)填充 0; - 主鍵約束(
PRIMARY KEY):唯一標(biāo)識(shí)記錄,非空且唯一; - 自增長(zhǎng)約束(
AUTO_INCREMENT):整數(shù)字段自動(dòng)遞增; - 唯一鍵約束(
UNIQUE KEY):字段值唯一,允許為空; - 外鍵約束(
FOREIGN KEY):關(guān)聯(lián)兩張表,保證數(shù)據(jù)邏輯一致性。
| 約束類型 | 描述 |
|---|---|
| 空屬性約束(NULL/NOT NULL) | 限制字段是否允許為空值。 |
| 默認(rèn)值約束(DEFAULT) | 字段未賦值時(shí)自動(dòng)使用默認(rèn)值。 |
| 列描述(COMMENT) | 用于字段說(shuō)明,沒(méi)有校驗(yàn)作用。 |
| 零填充約束(ZEROFILL) | 數(shù)字類型不足指定寬度時(shí)在前面填充零。 |
| 主鍵約束(PRIMARY KEY) | 唯一標(biāo)識(shí)表中的每一行記錄,字段值非空且唯一。 |
| 自增長(zhǎng)約束(AUTO_INCREMENT) | 整數(shù)字段在插入新記錄時(shí)自動(dòng)遞增。 |
| 唯一鍵約束(UNIQUE KEY) | 保證字段值唯一,但允許為空值(通常只允許一個(gè)空值)。 |
| 外鍵約束(FOREIGN KEY) | 用于關(guān)聯(lián)兩張表,保證數(shù)據(jù)的一致性和完整性。 |
二. 基礎(chǔ)約束:NULL/NOT NULL 與 DEFAULT
基礎(chǔ)約束主要控制字段的空值和默認(rèn)值,是表設(shè)計(jì)的基礎(chǔ)要求。
2.1 空屬性約束(NULL/NOT NULL)
NULL:默認(rèn)值,字段允許為空(空值無(wú)法參與運(yùn)算,如1+NULL=NULL);NOT NULL:字段不允許為空,插入 / 更新時(shí)必須賦值。
實(shí)戰(zhàn)案例:
創(chuàng)建班級(jí)表,要求班級(jí)名和教室不能為空:
-- 創(chuàng)建表(班級(jí)名和教室非空)
CREATE TABLE myclass(
class_name VARCHAR(20) NOT NULL,
class_room VARCHAR(10) NOT NULL
);
-- 插入合法數(shù)據(jù)(成功)
INSERT INTO myclass VALUES('class1', '301');
-- 插入非法數(shù)據(jù)(未給class_room賦值,報(bào)錯(cuò))
INSERT INTO myclass(class_name) VALUES('class2');
-- 報(bào)錯(cuò):ERROR 1364 (HY000): Field 'class_room' doesn't have a default value2.2 默認(rèn)值約束(DEFAULT)
當(dāng)字段經(jīng)常性出現(xiàn)某個(gè)固定值時(shí),可設(shè)置DEFAULT,插入時(shí)省略該字段則自動(dòng)使用默認(rèn)值。
實(shí)戰(zhàn)案例:
創(chuàng)建用戶表,年齡默認(rèn) 0,性別默認(rèn) “男”:
CREATE TABLE tt10(
name VARCHAR(20) NOT NULL, -- 非空(必須賦值)
age TINYINT UNSIGNED DEFAULT 0, -- 默認(rèn)0
sex CHAR(2) DEFAULT '男' -- 默認(rèn)男
);
-- 插入時(shí)僅賦值name,age和sex使用默認(rèn)值
INSERT INTO tt10(name) VALUES('zhangsan');
-- 查詢結(jié)果
SELECT * FROM tt10;
-- +----------+-----+-----+
-- | name | age | sex |
-- +----------+-----+-----+
-- | zhangsan | 0 | 男 |
-- +----------+-----+-----+注意:NOT NULL和DEFAULT一般不同時(shí)使用(DEFAULT已保證字段非空)。

2.3 列描述(COMMENT)
COMMENT用于描述字段含義,無(wú)實(shí)際校驗(yàn)作用,僅方便開(kāi)發(fā)者和 DBA 理解表結(jié)構(gòu),需通過(guò)SHOW CREATE TABLE查看。
CREATE TABLE tt12( name VARCHAR(20) NOT NULL COMMENT '姓名', age TINYINT UNSIGNED DEFAULT 0 COMMENT '年齡', sex CHAR(2) DEFAULT '男' COMMENT '性別' ); -- desc無(wú)法查看注釋 DESC tt12; -- 通過(guò)SHOW CREATE TABLE查看注釋 SHOW CREATE TABLE tt12\G -- 結(jié)果: -- `name` varchar(20) NOT NULL COMMENT '姓名', -- `age` tinyint(3) unsigned DEFAULT '0' COMMENT '年齡', -- `sex` char(2) DEFAULT '男' COMMENT '性別'
2.4 零填充約束(ZEROFILL)
僅對(duì)數(shù)字類型有效,當(dāng)字段值的寬度小于設(shè)定寬度時(shí),自動(dòng)在左側(cè)填充 0(僅格式化顯示,實(shí)際存儲(chǔ)值不變)。
實(shí)戰(zhàn)案例:
-- 創(chuàng)建表,a字段設(shè)置zerofill CREATE TABLE tt3( a INT(5) UNSIGNED ZEROFILL, -- 寬度5,零填充 b INT(10) UNSIGNED ); -- 插入數(shù)據(jù) INSERT INTO tt3 VALUES(1, 2); -- 查詢結(jié)果(a字段填充為00001) SELECT * FROM tt3; -- +-------+------+ -- | a | b | -- +-------+------+ -- | 00001 | 2 | -- +-------+------+ -- 驗(yàn)證實(shí)際存儲(chǔ)值(仍為1) SELECT a, HEX(a) FROM tt3; -- +-------+-------+ -- | a | HEX(a) | -- +-------+-------+ -- | 00001 | 1 | -- +-------+-------+
- 關(guān)鍵:無(wú)
ZEROFILL時(shí),數(shù)字類型后的寬度(如INT(10))毫無(wú)意義,僅用于顯示格式化。
三. 核心約束:主鍵、自增長(zhǎng)與唯一鍵
主鍵、自增長(zhǎng)、唯一鍵是保證數(shù)據(jù)唯一性的核心約束,解決 “重復(fù)數(shù)據(jù)” 和 “邏輯主鍵” 問(wèn)題。
3.1 主鍵約束(PRIMARY KEY)
主鍵是表的 “唯一標(biāo)識(shí)”,核心特性:
- 非空(
NOT NULL)且唯一(UNIQUE); - 一張表最多只能有一個(gè)主鍵;
- 通常用于標(biāo)識(shí)唯一記錄(如用戶 ID、學(xué)號(hào))。
實(shí)戰(zhàn)案例 1:?jiǎn)巫侄沃麈I
-- 創(chuàng)建表時(shí)指定主鍵 CREATE TABLE tt13( id INT UNSIGNED PRIMARY KEY COMMENT '學(xué)號(hào)(主鍵)', name VARCHAR(20) NOT NULL ); -- 插入合法數(shù)據(jù)(成功) INSERT INTO tt13 VALUES(1, 'aaa'); -- 插入重復(fù)主鍵(報(bào)錯(cuò)) INSERT INTO tt13 VALUES(1, 'bbb'); -- 報(bào)錯(cuò):ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
實(shí)戰(zhàn)案例 2:復(fù)合主鍵(多字段聯(lián)合主鍵)
當(dāng)單個(gè)字段無(wú)法唯一標(biāo)識(shí)記錄時(shí),可使用復(fù)合主鍵(多字段聯(lián)合唯一):
-- 學(xué)生成績(jī)表:id(學(xué)號(hào))+ course(課程代碼)為復(fù)合主鍵 CREATE TABLE tt14( id INT UNSIGNED, course CHAR(10) COMMENT '課程代碼', score TINYINT UNSIGNED DEFAULT 60 COMMENT '成績(jī)', PRIMARY KEY(id, course) -- 復(fù)合主鍵 ); -- 插入合法數(shù)據(jù)(成功) INSERT INTO tt14(id, course) VALUES(1, '123'); -- 插入重復(fù)復(fù)合主鍵(報(bào)錯(cuò)) INSERT INTO tt14(id, course) VALUES(1, '123'); -- 報(bào)錯(cuò):ERROR 1062 (23000): Duplicate entry '1-123' for key 'PRIMARY'
主鍵的添加與刪除
-- 給已有表添加主鍵 ALTER TABLE tt13 ADD PRIMARY KEY(id); -- 刪除主鍵(注意:若主鍵關(guān)聯(lián)自增長(zhǎng),需先取消自增長(zhǎng)) ALTER TABLE tt13 DROP PRIMARY KEY;
3.2 自增長(zhǎng)約束(AUTO_INCREMENT)
自增長(zhǎng)字段會(huì) 自動(dòng)從當(dāng)前最大值 + 1 生成新值,核心特性:
- 必須是整數(shù)類型;
- 必須是索引(
KEY一欄有值,通常與主鍵搭配); - 一張表最多只能有一個(gè)自增長(zhǎng)。
實(shí)戰(zhàn)案例:
-- 主鍵+自增長(zhǎng)(邏輯主鍵)
CREATE TABLE tt21(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(10) NOT NULL DEFAULT ''
);
-- 插入時(shí)省略id,自動(dòng)遞增
INSERT INTO tt21(name) VALUES('a');
INSERT INTO tt21(name) VALUES('b');
-- 查詢結(jié)果
SELECT * FROM tt21;
-- +----+------+
-- | id | name |
-- +----+------+
-- | 1 | a |
-- | 2 | b |
-- +----+------+
-- 獲取上次插入的自增長(zhǎng)值
SELECT LAST_INSERT_ID(); -- 結(jié)果:1(批量插入返回第一個(gè)值)
3.3 唯一鍵約束(UNIQUE KEY)
唯一鍵用于保證字段值唯一,但允許為空(空值不參與唯一性比較),解決 “一張表多個(gè)唯一字段” 的需求(主鍵僅能有一個(gè))。
- 主鍵與唯一鍵的區(qū)別:
以下是生成的表格:
| 特性 | 主鍵(PRIMARY KEY) | 唯一鍵(UNIQUE KEY) |
|---|---|---|
| 唯一性 | 是 | 是 |
| 非空性 | 是 | 否(允許空值) |
| 一張表數(shù)量 | 最多 1 個(gè) | 多個(gè) |
| 核心作用 | 標(biāo)識(shí)唯一記錄 | 保證業(yè)務(wù)字段不重復(fù) |
實(shí)戰(zhàn)案例:
創(chuàng)建學(xué)生表,學(xué)號(hào)唯一(允許為空):
CREATE TABLE student(
id CHAR(10) UNIQUE COMMENT '學(xué)號(hào)(唯一,可空)',
name VARCHAR(10)
);
-- 插入合法數(shù)據(jù)(成功)
INSERT INTO student(id, name) VALUES('01', 'aaa');
INSERT INTO student(id, name) VALUES(NULL, 'bbb'); -- 空值允許
-- 插入重復(fù)唯一鍵(報(bào)錯(cuò))
INSERT INTO student(id, name) VALUES('01', 'ccc');
-- 報(bào)錯(cuò):ERROR 1062 (23000): Duplicate entry '01' for key 'id'四. 關(guān)聯(lián)約束:外鍵(FOREIGN KEY)
外鍵用于定義主表和從表的關(guān)聯(lián)關(guān)系,保證數(shù)據(jù)的邏輯一致性(如學(xué)生的班級(jí)必須存在于班級(jí)表中)。
4.1 外鍵核心規(guī)則
- 外鍵定義在從表上,主表必須有主鍵或唯一鍵;
- 從表外鍵字段的值必須在主表對(duì)應(yīng)字段中存在,或?yàn)镹ULL;
- 主表刪除 / 修改關(guān)聯(lián)記錄時(shí),需處理從表關(guān)聯(lián)數(shù)據(jù)(如級(jí)聯(lián)刪除、拒絕操作)。

4.2 實(shí)戰(zhàn)案例
創(chuàng)建班級(jí)表(主表)和學(xué)生表(從表),學(xué)生表的class_id關(guān)聯(lián)班級(jí)表的id:
-- 1. 創(chuàng)建主表(班級(jí)表) CREATE TABLE myclass( id INT PRIMARY KEY, -- 主鍵 name VARCHAR(30) NOT NULL COMMENT '班級(jí)名' ); -- 2. 創(chuàng)建從表(學(xué)生表),添加外鍵 CREATE TABLE stu( id INT PRIMARY KEY, name VARCHAR(30) NOT NULL COMMENT '學(xué)生名', class_id INT, -- 外鍵:class_id關(guān)聯(lián)myclass的id FOREIGN KEY (class_id) REFERENCES myclass(id) ); -- 3. 插入主表數(shù)據(jù) INSERT INTO myclass VALUES(10, 'C++大牛班'), (20, 'Java大神班'); -- 4. 插入合法從表數(shù)據(jù)(class_id在主表存在) INSERT INTO stu VALUES(100, '張三', 10), (101, '李四', 20); -- 5. 插入非法從表數(shù)據(jù)(class_id=30在主表不存在,報(bào)錯(cuò)) INSERT INTO stu VALUES(102, '王五', 30); -- 報(bào)錯(cuò):ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails -- 6. 插入class_id=NULL(未分配班級(jí),成功) INSERT INTO stu VALUES(102, '趙六', NULL);
4.3 外鍵的意義
外鍵的核心價(jià)值是 “讓數(shù)據(jù)庫(kù)自動(dòng)校驗(yàn)數(shù)據(jù)關(guān)聯(lián)性”,避免出現(xiàn)邏輯沖突(如不存在的班級(jí)、不存在的用戶訂單)。若不設(shè)置外鍵,需在程序中手動(dòng)校驗(yàn),增加開(kāi)發(fā)成本且易出錯(cuò)。

五. 綜合實(shí)戰(zhàn):設(shè)計(jì)電商訂單表
結(jié)合所有約束,設(shè)計(jì)電商系統(tǒng)的商品表、客戶表、購(gòu)買(mǎi)表,滿足以下需求:
- 商品表:商品編號(hào)自增主鍵,名稱非空,單價(jià)默認(rèn) 0;
- 客戶表:客戶編號(hào)自增主鍵,姓名非空,郵箱唯一,性別枚舉(男 / 女),身份證唯一;
- 購(gòu)買(mǎi)表:訂單號(hào)自增主鍵,關(guān)聯(lián)客戶編號(hào)和商品編號(hào)(外鍵),購(gòu)買(mǎi)數(shù)量默認(rèn) 0。
-- 創(chuàng)建數(shù)據(jù)庫(kù)
CREATE DATABASE IF NOT EXISTS bit32mall DEFAULT CHARACTER SET utf8;
USE bit32mall;
-- 1. 商品表(主表)
CREATE TABLE IF NOT EXISTS goods(
goods_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '商品編號(hào)',
goods_name VARCHAR(32) NOT NULL COMMENT '商品名稱',
unitprice FLOAT NOT NULL DEFAULT 0.1 COMMENT '單價(jià)(單位:分)',
category VARCHAR(12) COMMENT '商品分類', // 也可以用枚舉
provider VARCHAR(64) NOT NULL COMMENT '供應(yīng)商名稱' // 也可以用枚舉
);
-- 2. 客戶表(主表)
CREATE TABLE IF NOT EXISTS customer(
customer_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '客戶編號(hào)',
name VARCHAR(32) NOT NULL COMMENT '客戶姓名',
address VARCHAR(256) COMMENT '客戶地址',
email VARCHAR(64) UNIQUE KEY COMMENT '電子郵箱(唯一)',
sex ENUM('男','女') NOT NULL COMMENT '性別',
card_id CHAR(18) UNIQUE KEY COMMENT '身份證(唯一)'
);
-- 3. 購(gòu)買(mǎi)表(從表,關(guān)聯(lián)客戶表和商品表)
CREATE TABLE IF NOT EXISTS purchase(
order_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '訂單號(hào)',
customer_id INT COMMENT '客戶編號(hào)',
goods_id INT COMMENT '商品編號(hào)',
nums INT DEFAULT 1 COMMENT '購(gòu)買(mǎi)數(shù)量',
-- 外鍵關(guān)聯(lián)
FOREIGN KEY (customer_id) REFERENCES customer(customer_id),
FOREIGN KEY (goods_id) REFERENCES goods(goods_id)
);六. 約束選型避坑指南和總結(jié)
- 優(yōu)先使用非空約束:盡量讓字段
NOT NULL,空值會(huì)導(dǎo)致查詢條件(如WHERE age=0)失效,且無(wú)法參與運(yùn)算; - 主鍵選擇邏輯 ID:主鍵建議用與業(yè)務(wù)無(wú)關(guān)的自增整數(shù)(如
id),避免用身份證、手機(jī)號(hào)等業(yè)務(wù)字段(需頻繁修改); - 唯一鍵保證業(yè)務(wù)唯一性:如郵箱、身份證等業(yè)務(wù)字段,用
UNIQUE KEY約束,而非主鍵; - 外鍵謹(jǐn)慎使用:外鍵會(huì)降低表的插入 / 更新性能,高并發(fā)場(chǎng)景可去掉外鍵,在程序中校驗(yàn)關(guān)聯(lián)性;
- 自增長(zhǎng)字段注意重置:刪除表中數(shù)據(jù)后,自增長(zhǎng)值不會(huì)自動(dòng)重置,需用
ALTER TABLE表名AUTO_INCREMENT=1手動(dòng)重置。
總結(jié):
MySQL 表約束是數(shù)據(jù)完整性的核心保障,從基礎(chǔ)的空值 / 默認(rèn)值約束,到核心的主鍵 / 唯一鍵約束,再到關(guān)聯(lián)的外鍵約束,各自解決不同場(chǎng)景的問(wèn)題:
- 基礎(chǔ)約束(NULL/DEFAULT/COMMENT/ZEROFILL):控制字段基礎(chǔ)屬性;
- 核心約束(PRIMARY KEY/AUTO_INCREMENT/UNIQUE KEY):保證數(shù)據(jù)唯一性;
- 關(guān)聯(lián)約束(FOREIGN KEY):保證表間數(shù)據(jù)邏輯一致。
到此這篇關(guān)于MySQL 表約束從基礎(chǔ)約束到外鍵關(guān)聯(lián)實(shí)戰(zhàn)案例詳解的文章就介紹到這了,更多相關(guān)mysql表約束內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
高版本Mysql使用group?by分組報(bào)錯(cuò)的解決方案
GROUP?BY?語(yǔ)句用于結(jié)合合計(jì)函數(shù),根據(jù)一個(gè)或多個(gè)列對(duì)結(jié)果集進(jìn)行分組,下面這篇文章主要給大家介紹了關(guān)于高版本Mysql使用group?by分組報(bào)錯(cuò)的解決方案,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03
mysql函數(shù)日期和時(shí)間函數(shù)匯總
這篇文章主要介紹了mysql函數(shù)日期和時(shí)間函數(shù)匯總,日期和時(shí)間函數(shù)主要用來(lái)處理日期和時(shí)間值,一般的日期函數(shù)除了使用??date???類型的參數(shù)外,也可以使用??datetime???或者??timestamp??類型的參數(shù),但會(huì)忽略這些值的時(shí)間部分2022-07-07
MySQL使用innobackupex備份連接服務(wù)器失敗的解決方法
這篇文章主要為大家詳細(xì)介紹了MySQL使用innobackupex備份連接服務(wù)器失敗的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02
Mysql之DELETE操作對(duì)應(yīng)的undo日志方式
InnoDB通過(guò)正常記錄鏈表和垃圾鏈表管理數(shù)據(jù)頁(yè),刪除記錄先標(biāo)記為deletemark,后由purge線程移入垃圾鏈表回收空間,PAGE_FREE指向垃圾鏈表頭,PAGE_GARBAGE記錄可重用空間大小,Undo日志保存trx_id和roll_pointer,用于版本控制和回滾2025-08-08
MySQL學(xué)習(xí)(七):Innodb存儲(chǔ)引擎索引的實(shí)現(xiàn)原理詳解
這篇文章主要介紹了Innodb存儲(chǔ)引擎索引的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
Mysql清空表數(shù)據(jù)庫(kù)命令truncate和delete詳解
這篇文章主要介紹了Mysql數(shù)據(jù)庫(kù)清空表truncate和delete的相關(guān)知識(shí),本文給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06

