MySQL中的約束Constraint解讀
約束簡(jiǎn)介
約束是我們?cè)趧?chuàng)建表的時(shí)候, 我們可以給表中的字段添加約束確保我們的數(shù)據(jù)的完整性和有效性, 比如大家平時(shí)上網(wǎng)時(shí)注冊(cè)用戶(hù)常見(jiàn)的 : 用戶(hù)名不能為空, 對(duì)不起, 用戶(hù)名已經(jīng)存在等提示信息
約束通常包括下面的這6種
約束類(lèi)型 | 約束關(guān)鍵字 |
---|---|
非空約束 | not null |
默認(rèn)約束 | default |
檢查約束 | check |
唯一約束 | unique |
主鍵約束 | primary key |
外鍵約束 | foreign key |
下面我們會(huì)詳細(xì)的剖析這幾種約束(默認(rèn)約束省略, 就是一個(gè)簡(jiǎn)單的在不插入這個(gè)字段的數(shù)據(jù)時(shí)插入默認(rèn)值)
非空約束
這個(gè)約束比較好理解, 就是插入的數(shù)據(jù)不能為空, 當(dāng)我們?cè)O(shè)置這一個(gè)約束的時(shí)候, 我們使用desc展示表結(jié)構(gòu)的時(shí)候, 表的Null那一行就會(huì)設(shè)置為NO, 允許為空就會(huì)為YES, 下面是我們的一個(gè)實(shí)例
+--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | DEPTNO | int | NO | PRI | NULL | | | DNAME | varchar(14) | YES | | NULL | | | LOC | varchar(13) | YES | | NULL | | +--------+-------------+------+-----+---------+-------+
嘗試完成下面的一個(gè)需求, 創(chuàng)建一個(gè)學(xué)校表, 有編號(hào), 學(xué)校名稱(chēng)(不能為空), 建校時(shí)間, 這時(shí)候就需要給name字段加上一個(gè)非空約束not null
我們嘗試執(zhí)行下面的SQL語(yǔ)句
-- 創(chuàng)建一個(gè)學(xué)校表 create table school( sno int comment '學(xué)校編號(hào)', name varchar(255) not null comment '學(xué)校名稱(chēng)', est_time date comment '建校時(shí)間' ); -- 插入幾條數(shù)據(jù)來(lái)進(jìn)行測(cè)試(關(guān)于日期類(lèi)型的插入, 其實(shí)底層會(huì)進(jìn)行str_to_date函數(shù)的調(diào)用, 把字符串轉(zhuǎn)化為日期) insert into school (sno, name, est_time) values (1, '北京101中學(xué)', '1910-08-11'); insert into school (sno, name, est_time) values (2, '南開(kāi)附中', '1912-04-13'); insert into school (sno, name, est_time) values (3, '清華附中', '1915-03-12'); -- select 查詢(xún)一下當(dāng)前的信息 select * from school; -- 執(zhí)行結(jié)果如下 +------+-----------------+------------+ | sno | name | est_time | +------+-----------------+------------+ | 1 | 北京101中學(xué) | 1910-08-11 | | 2 | 南開(kāi)附中 | 1912-04-13 | | 3 | 清華附中 | 1915-03-12 | +------+-----------------+------------+
由于我們添加了非空約束, 也就是我們的name不可以為null, 如果插入一條數(shù)據(jù)沒(méi)有name就會(huì)報(bào)錯(cuò)
-- 嘗試執(zhí)行下面的SQL insert into school (sno, est_time) values (4, '1899-11-06'); -- 會(huì)直接報(bào)錯(cuò), 報(bào)錯(cuò)信息如下 ERROR 1364 (HY000): Field 'name' doesn't have a default value
檢查約束
其實(shí)就是在創(chuàng)建一張表的時(shí)候添加一定的檢查信息, 這個(gè)約束時(shí)MySQL8版本之后新添加的, 在先前的版本中是不存在的
基礎(chǔ)語(yǔ)法
create table [表名]( ...字段信息 check(約束條件) );
我們嘗試建一張學(xué)生表, 要求添加學(xué)生的年齡必須大于18歲, 這種情況就可以使用檢查約束
-- 創(chuàng)建一張學(xué)生表 create table t_stu( sno int, name varchar(255), age int, check(age > 18) ); -- 執(zhí)行DML語(yǔ)句 insert into t_stu(sno, name, age) values (1, 'hh', 19); insert into t_stu(sno, name, age) values (2, 'xx', 18); -- 第一條是執(zhí)行成功的, 但是第二條 18 == 18, 不滿(mǎn)足check約束, 所以失敗
唯一約束
對(duì)一個(gè)字段添加unique約束, 這個(gè)字段就具有了唯一性, 唯一性的字段是可以為null, 但不可以重復(fù), 如果是null的話(huà), 是可以重復(fù)的, 我們拿下面的t_stu表作為一個(gè)例子說(shuō)明
# 創(chuàng)建了一個(gè)學(xué)生表, 這個(gè)表的email字段是唯一的, 不可以重復(fù) create table t_stu( no int, name varchar(255), email varchar(255) unique ); # 我們嘗試執(zhí)行下面的DML語(yǔ)句, 執(zhí)行成功成功的標(biāo)明 √, 否則為 × insert into t_stu (no, name, email) values (1, 'hh', 'hh@163.com'); insert into t_stu (no, name, email) values (2, 'xx', 'xx@163.com'); insert into t_stu (no, name) values (3, 'xx'), (4, 'wx'); # 查看一下當(dāng)前的信息 select * from t_stu; +------+------+------------+ | no | name | email | +------+------+------------+ | 1 | hh | hh@163.com | | 2 | xx | xx@163.com | | 3 | xx | NULL | | 4 | wx | NULL | +------+------+------------+ # 嘗試插入一條重復(fù)的數(shù)據(jù) insert into t_stu (no, name, email) values (5, 'sf', 'hh@163.com'); # 直接報(bào)錯(cuò), 因?yàn)閑mail的位置重復(fù)了
unique約束也可以和not null 同時(shí)使用表示非空且唯一
create table t_stu( no int, name varchar(255), email varchar(255) not null unique );
列級(jí)約束與表級(jí)約束
如果一個(gè)約束緊緊的跟在字段后面, 那這個(gè)字段僅僅作用于這一個(gè)字段, 我們稱(chēng)之為列級(jí)約束, 如果一個(gè)約束位于表定義的結(jié)尾位置, 那么這個(gè)約束我們稱(chēng)之為表級(jí)約束, 這種約束可以約束多種字段
unique, primary key, foreign key 可以作為表級(jí)約束存在, not null 不可以作為表級(jí)約束
我們比較一下下面的SQL語(yǔ)句
# unique的列級(jí)約束 create table t_stu( no int, name varchar(255), email varchar(255), unique(email) ); # unique的表級(jí)約束 create table t_stu( no int, name varchar(255), email varchar(255), unique(name,email) );
那么上述列級(jí)和表級(jí)的約束有什么區(qū)別呢?
答案是 : 表級(jí)可以聯(lián)合聯(lián)合多個(gè)字段而列級(jí)只能約束一個(gè)字段
給約束起名字
約束是以對(duì)象的形式存在的, 所有的約束對(duì)象對(duì)象都存在一個(gè)系統(tǒng)表中
information_schema(四個(gè)系統(tǒng)數(shù)據(jù)庫(kù)之一)中的table_constraints這個(gè)表中, 這張表保存的所有的約束名稱(chēng)信息
這里注意, 列級(jí)約束是不能起名字的, 但是有系統(tǒng)默認(rèn)分配的名字, 只有表級(jí)別才可以起名字, 如果不起名字的話(huà), 也會(huì)自動(dòng)分配一個(gè)
找到約束的名字我們就可以對(duì)約束進(jìn)行刪除, 從而消除對(duì)某些字段的約束
首先找到這張表
使用這個(gè)information_schema數(shù)據(jù)庫(kù)
這里面有79張表, 找到這個(gè)TABLE_CONSTRAINTS表, 這個(gè)表存儲(chǔ)的所有的約束對(duì)象的信息
desc查看一下表結(jié)構(gòu), 我們找到剛才的 t_stu 學(xué)生表的約束名稱(chēng)
先用DDL語(yǔ)句查看一下創(chuàng)建 t_stu 這張表時(shí)的建表語(yǔ)句
我們可以看到, 我們創(chuàng)建表的時(shí)候?qū)mail字段進(jìn)行了unique約束, 但是沒(méi)有給約束起名字, 所以系統(tǒng)會(huì)自動(dòng)分配名字
下面我們查看一下這個(gè)約束的名稱(chēng)
這里可以看到這個(gè)約束的名稱(chēng)時(shí)email
下面我們創(chuàng)建一張新的表, 從新添加一個(gè)約束并給約束起名字
基礎(chǔ)的語(yǔ)法如下
# 表級(jí)約束起名的語(yǔ)法 constraint [約束名稱(chēng)] [表級(jí)約束的主體]; # 起約束名的標(biāo)準(zhǔn) 表名_約束的字段_約束的簡(jiǎn)稱(chēng)(unique/pk/fk)
創(chuàng)建一個(gè)班級(jí)表進(jìn)行測(cè)試
-- 創(chuàng)建一個(gè)班級(jí)表(設(shè)置班級(jí)編號(hào)為主鍵, 班級(jí)名稱(chēng)為唯一鍵) create table class( cno int comment '班級(jí)編號(hào)', cname varchar(255) comment '班級(jí)名稱(chēng)', constraint class_cno_pk primary key(cno), constraint class_cname_unique unique(cname) );
用上面我們找到約束的名稱(chēng)的流程進(jìn)行演示…
# 使用一下這個(gè)系統(tǒng)庫(kù) use information_schema; # 找到class表的約束名稱(chēng) select constraint_name from table_constraints where table_name = 'class';
這里可能會(huì)有疑問(wèn)為什么給主鍵起的名字沒(méi)有生效呢?
下面是我查閱的資料
MySQL版本限制:從MySQL8.0版本開(kāi)始,主鍵的名字不再可以直接修改。這是由于 MySQL的內(nèi)部存儲(chǔ)引擎(如InnoDB)實(shí)現(xiàn)方式導(dǎo)致的,InnoDB存儲(chǔ)引擎中主鍵的名字是以索引的形式存儲(chǔ)的,修改主鍵名字實(shí)際上是修改索引的名字,會(huì)對(duì)存儲(chǔ)引擎的內(nèi)部數(shù)據(jù)結(jié)構(gòu)產(chǎn)生影響,因此MySQL禁止直接修改主鍵的名字。
但是經(jīng)過(guò)測(cè)試, 給外鍵起名字是生效的
主鍵約束
主鍵概念以及注意事項(xiàng)
主鍵約束是一個(gè)比較重要的內(nèi)容
- 主鍵 : primary key, 簡(jiǎn)稱(chēng)pk
- 主鍵約束的字段不能為NULL, 并且不可以重復(fù)
- 任何一張表都應(yīng)該有主鍵(第一范式), 沒(méi)有主鍵的表可以被視為無(wú)效表
- 主鍵值是這行記錄的身份證號(hào),是唯一標(biāo)識(shí)。在數(shù)據(jù)庫(kù)表中即使兩條數(shù)據(jù)一模一樣,但由于主鍵值不同,我們也會(huì)認(rèn)為是兩條完全的不同的數(shù)據(jù)。
- 主鍵分類(lèi):
單一主鍵(建議使用這種方式)
create table t_student( id bigint primary key, sno varchar(255) unique, sname varchar(255) not null )
聯(lián)合主鍵(很少用, 了解)
create table t_user( no int, name varchar(255), age int, primary key(no,name) );
主鍵自增
既然主鍵值是一個(gè)自然的數(shù)字,mysql為主鍵值提供了一種自增機(jī)制,不需要我們程序員維護(hù),mysql自動(dòng)維護(hù)該字段鍵自增
create table t_vip( no int primary key auto_increment, name varchar(255) );
外鍵約束
外鍵概念以及注意事項(xiàng)
外鍵約束
- 外鍵約束 : foreign key, 簡(jiǎn)稱(chēng)fk
- 添加了外鍵約束的字段的數(shù)據(jù)必須來(lái)源于其他的其他字段, 不可以隨便設(shè)置
- 比如我們給a字段添加了外鍵約束, 要求a字段中的數(shù)據(jù)必須來(lái)源于b字段b字段不一定是主鍵, 但一定具有唯一性
- 外鍵約束可以給單個(gè)字段添加,叫做單一外鍵。也可以給多個(gè)字段聯(lián)合添加,叫做復(fù)合外鍵。復(fù)合外鍵 很少用。
- 如果a表引用了b表的數(shù)據(jù)(a是外鍵, b具有唯一性被引用)
操作類(lèi)型 | 執(zhí)行順序 |
---|---|
創(chuàng)建表時(shí) | 先創(chuàng)建b, 再創(chuàng)建a |
插入數(shù)據(jù)時(shí) | 先插入b, 再插入a |
刪除數(shù)據(jù)時(shí) | 先刪除a, 再刪除b |
刪除表時(shí) | 先刪除a, 再刪除b |
外鍵使用場(chǎng)景
有下面一個(gè)需求, 我們想要?jiǎng)?chuàng)建一個(gè)學(xué)生表, 能夠存儲(chǔ)學(xué)生的信息
我們的第一種設(shè)計(jì)是這樣的
很明顯, 這樣創(chuàng)建表的方法會(huì)導(dǎo)致數(shù)據(jù)冗余(實(shí)質(zhì)上是違法了我們第三范式)
這張表是一種典型的一對(duì)多的情況, 所以根據(jù)創(chuàng)建表的設(shè)計(jì)(后面會(huì)講)我們創(chuàng)建為兩張表
一張學(xué)生表, 單獨(dú)存儲(chǔ)學(xué)生的信息, 另一張是學(xué)校表, 專(zhuān)門(mén)存儲(chǔ)學(xué)校的相關(guān)信息, 返回用外鍵進(jìn)行關(guān)聯(lián)
為什么要設(shè)置外鍵約束, 因?yàn)閷?duì)于學(xué)生來(lái)說(shuō), 學(xué)校編號(hào)這一個(gè)條件不是隨便設(shè)置的, 要用學(xué)校表中的信息進(jìn)行約束
有了上面的鋪墊, 我們嘗試創(chuàng)建一個(gè)學(xué)生表, 和一個(gè)學(xué)校表
-- 創(chuàng)建一個(gè)學(xué)校表(把學(xué)校編號(hào)設(shè)置為主鍵) create table t_school( sch_no int primary key auto_increment, sch_name varchar(255) unique ); -- 創(chuàng)建一個(gè)學(xué)生表(把學(xué)生編號(hào)設(shè)置為主鍵, 然后名字添加default約束, 年齡添加check約束, 學(xué)校名稱(chēng)為外鍵) create table t_student( stu_no int primary key auto_increment, stu_name varchar(255) default '無(wú)名氏', age int, sch_no int, check(age >= 18), constraint t_student_sch_no_fk foreign key(sch_no) references t_school(sch_no) ); -- 插入幾條測(cè)試數(shù)據(jù)(按照我們舉出來(lái)的例子) insert into t_school (sch_name) values ('清華大學(xué)'), ('北京大學(xué)'), ('浙江大學(xué)'), ('復(fù)旦大學(xué)'); insert into t_student (stu_name, age, sch_no) values ('hxh', 19, 1), ('dwv', 18, 1), ('cac', 19, 1), ('fqe', 20, 1); insert into t_student (stu_name, age, sch_no) values ('ger', 18, 2), ('he', 21, 2); insert into t_student (stu_name, age, sch_no) values ('few', 20, 3), ('rhr', 22, 3), ('wgh', 22, 3); insert into t_student (stu_name, age, sch_no) values ('rhre', 22, 4), ('wg', 21, 4);
約束的刪除與添加
由于我們下面要介紹級(jí)聯(lián)的相關(guān)操作, 所以要?jiǎng)h除之前的外鍵約束條件, 我們之前就說(shuō)過(guò), 可以通過(guò)找到約束的名字從而刪除約束, 這種操作其實(shí)是DDL, 對(duì)表層面的一種操作, 我們首先找到t_student表的約束的名字
刪除約束的基礎(chǔ)語(yǔ)法如下
# 基礎(chǔ)語(yǔ)法就是 alter table [表名] drop constraint [約束名稱(chēng)]; # 刪除學(xué)生表的外鍵約束 alter table t_student constraint t_student_sch_no_fp;
約束添加的語(yǔ)法與刪除的語(yǔ)法類(lèi)似 , 都是DDL語(yǔ)句那一套邏輯
# 基礎(chǔ)語(yǔ)法 alter table [表名] add constraint [約束名稱(chēng)] [約束主體]; # 比如我們從新把剛才刪除外鍵約束添加回來(lái) alter table t_student add constraint stu_pk foreign key(sch_no) references t_school(sch_no);
級(jí)聯(lián)相關(guān)操作
我們上面介紹外鍵概念的時(shí)候提到過(guò), 如果刪除表或者刪除表中的數(shù)據(jù)的時(shí)候要注意先后的順序
那有沒(méi)有一種方法, 可以在操縱主表的同時(shí), 同時(shí)修改副表里面關(guān)聯(lián)的數(shù)據(jù)呢
級(jí)聯(lián)刪除(on delete cascade)
我們從新把上面創(chuàng)建的學(xué)校表和學(xué)生表拿出來(lái)
現(xiàn)在存在外鍵約束, 我們直接刪除學(xué)校編號(hào)為4的復(fù)旦大學(xué)是不能操作的, 因?yàn)橛懈北淼臄?shù)據(jù)引用
但是通過(guò)級(jí)聯(lián)刪除就可以實(shí)現(xiàn)這一效果(刪除主表信息的同時(shí)刪除副表內(nèi)容)
基礎(chǔ)語(yǔ)法
-- 只需要在創(chuàng)建約束的時(shí)候在末端加上一個(gè) (on delete cascade) [創(chuàng)建約束主體] on delete cascade;
下面我們進(jìn)行測(cè)試
-- 首先刪除一下之前的外鍵約束 alter table t_student drop constraint stu_pk; -- 創(chuàng)建一個(gè)新的外鍵約束(并加上一個(gè)級(jí)聯(lián)刪除的信息) alter table t_student add constraint t_student_sch_no_fk foreign key( sch_no) references t_school(sch_no) on delete cascade; -- 下面我們從新進(jìn)行測(cè)試 delete from t_school where sch_no = 4; -- 此時(shí)就會(huì)顯示執(zhí)行成功, 我們從新查看一下學(xué)生表中的數(shù)據(jù), 執(zhí)行結(jié)果如下圖
注意, 級(jí)聯(lián)刪除其實(shí)是一個(gè)相當(dāng)危險(xiǎn)的操作
級(jí)聯(lián)更新(on update cascade)
作用就是當(dāng)更新主表數(shù)據(jù)的同時(shí)修改副表中的相關(guān)數(shù)據(jù)
基礎(chǔ)語(yǔ)法
[約束主體] on update cascade;
下面是測(cè)試內(nèi)容
-- 首先還是一下上一個(gè)約束 alter table t_student drop constraint t_student_sch_no_fk; -- 創(chuàng)建一個(gè)級(jí)聯(lián)更新約束 alter table t_student add constraint t_student_sch_no_fk foreign key(sch_no) references t_school(sch_no) on update cascade; -- 把學(xué)校編號(hào)為3改為學(xué)校編號(hào)為5 update t_school set sch_no = 5 where sch_no = 3; -- 查看一下當(dāng)前的學(xué)生信息 select * from t_student; -- 執(zhí)行結(jié)果見(jiàn)下圖
級(jí)聯(lián)置空(on delete set null)
作用就是當(dāng)在主表中刪除一個(gè)數(shù)據(jù)的時(shí)候, 副表中跟這條數(shù)據(jù)關(guān)聯(lián)的外鍵位置設(shè)置為NULL
基礎(chǔ)語(yǔ)法
[約束主體] on delete set null;
下面是我們的測(cè)試代碼
--還是先刪除約束 alter table t_student drop constraint t_student_sch_no_fk; -- 加一個(gè)級(jí)聯(lián)置空的約束 alter table t_student add constraint t_student_sch_no_fk foreign key(sch_no) references t_school(sch_no) on delete set null; -- 上一個(gè)級(jí)聯(lián)更新我們不是3編號(hào)更新為5了么, 我們現(xiàn)在刪除5數(shù)據(jù)對(duì)應(yīng)的編號(hào)學(xué)校 delete from t_school where sch_no = 5; -- 執(zhí)行結(jié)果見(jiàn)下圖
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
全面解析MySQL?Explain如何優(yōu)化SQL查詢(xún)性能
在?MySQL?中,EXPLAIN?關(guān)鍵字可以幫助我們分析查詢(xún)執(zhí)行計(jì)劃,從而優(yōu)化查詢(xún)性能,所以本文就來(lái)和大家詳細(xì)講講Explain是如何優(yōu)化SQL查詢(xún)性能的2023-05-05MySQL數(shù)據(jù)庫(kù)的卸載與安裝(Linux?Centos)
如果大家曾經(jīng)安裝過(guò)MySQL,現(xiàn)在想要更新MySQL的版本或者因?yàn)槟承┰驅(qū)е滦枰匮bMySQL,請(qǐng)記住重裝之前一定要把之前的MySQL版本卸載干凈,這篇文章主要給大家介紹了關(guān)于MySQL數(shù)據(jù)庫(kù)的卸載與安裝的相關(guān)資料,需要的朋友可以參考下2024-05-05mysql 8.0.15 安裝配置方法圖文教程(Windows10 X64)
這篇文章主要為大家詳細(xì)介紹了Windows10 X64 mysql 8.0.15 安裝配置方法圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-03-03MySQL中查詢(xún)的有關(guān)英文字母大小寫(xiě)問(wèn)題的分析
這篇文章主要介紹了MySQL中查詢(xún)的有關(guān)英文字母大小寫(xiě)問(wèn)題的分析,作者從多個(gè)角度進(jìn)行了實(shí)驗(yàn),需要的朋友可以參考下2015-05-05mysql 搜尋附近N公里內(nèi)數(shù)據(jù)的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇mysql 搜尋附近N公里內(nèi)數(shù)據(jù)的簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04MySQL 8.0.18給數(shù)據(jù)庫(kù)添加用戶(hù)和賦權(quán)問(wèn)題
這篇文章主要介紹了MySQL 8.0.18給數(shù)據(jù)庫(kù)添加用戶(hù)和賦權(quán)問(wèn)題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12MyEclipse連接MySQL數(shù)據(jù)庫(kù)報(bào)錯(cuò)解決辦法
我們現(xiàn)在一般網(wǎng)站都是利用的MySQL數(shù)據(jù)庫(kù)搭建網(wǎng)站的,但是在網(wǎng)上看到很多網(wǎng)友吐槽數(shù)據(jù)庫(kù)連接不上的問(wèn)題,現(xiàn)在我就結(jié)合相關(guān)資料向提出一些我個(gè)人的見(jiàn)解,希望對(duì)大家解決問(wèn)題有幫助2014-01-01