1. 程式人生 > 資料庫 >MYSQL的約束與設計

MYSQL的約束與設計

目錄

1. SQL約束

SQL 約束用於規定表中的資料規則,對錶中的資料進行限制,保證資料的正確性、有效性和完整性。一個表如果添加了約束,不正確的資料將無法插入到表中。

約束可以在建立表時規定(通過 CREATE TABLE 語句),或者在表建立之後規定(通過 ALTER TABLE 語句)。

SQL約束有以下5種:

image-20201118215923723

1.1 主鍵約束

主鍵用來唯一標識資料庫中的每一條記錄,例如下圖兩條記錄的欄位值都是一樣的,就需要主鍵id來給它們做唯一標識來區分。

image-20201118220357448

通常不用業務欄位作為主鍵,單獨給每張表設計一個 id 的欄位,把 id 作為主鍵。主鍵是給資料庫和程式使用 的,不是給最終的客戶使用的。所以主鍵有沒有含義沒有關係,只要不重複,非空就行。

如:身份證,學號不建議做成主鍵

(1)建立主鍵

主鍵的特點是:非空(not null)、唯一。建立主鍵後,往表裡插入資料後,必須給主鍵賦值,並且主鍵值不能與表裡的重複。

-- 建立主鍵有兩種方式:
-- 1.在建立表的時候給欄位新增主鍵,格式:欄位名 欄位型別 PRIMARY KEY
create table test (
 id int primary key, -- id 為主鍵
 name varchar(20),
 age int
)
-- 2.在已建好的表新增主鍵,格式:ALTER TABLE 表名 ADD PRIMARY KEY(欄位名);
alter table test add primary key(id);

(2)刪除主鍵

alter table test drop primary key;

(3)主鍵設定自增長

主鍵如果讓我們自己新增很有可能重複,我們通常希望在每次插入新記錄時,資料庫自動生成主鍵欄位的值。

create table test (
 id int primary key auto_increment, -- id 為主鍵,並且自增長
 name varchar(20),
 age int
)

主鍵設定自增長後,插入資料就無需再插入主鍵列了。

-- 主鍵自增後插入資料
insert into test (name,age) values ('tom',18);
insert into test (name,age) values ('cat',20);
-- 另一種寫法
insert into test values(null,'peter',35);

(4)修改自增長的預設值起始值

預設地 AUTO_INCREMENT 的開始值是 1,如果希望修改起始值,請使用下列 SQL 語法:

-- 1.建立表時指定起始值,格式 :
-- CREATE TABLE 表名(
-- 	列名 int primary key AUTO_INCREMENT
-- ) AUTO_INCREMENT=起始值;
-- 指定起始值為 1000
create table test (
 id int primary key auto_increment,
 name varchar(20)
) auto_increment = 1000;
-- 2.建立好表以後修改起始值
-- 格式:ALTER TABLE 表名 AUTO_INCREMENT=起始值;
-- 將建立好的表的起始值改為2000
alter table test auto_increment = 2000;

1.2 唯一約束

什麼是唯一約束: 表中某一列不能出現重複的值。

-- 1.建立學生表 test, 包含欄位(id, name),name 這一列設定唯一約束,不能出現同名的學生
-- 格式:欄位名 欄位型別 UNIQUE
create table test (
 id int,
 name varchar(20) unique
)
-- 新增一個同名的學生
insert into test values (1, '張三');
insert into test values (2, '張三');-- 報錯: Duplicate entry '張三' for key 'name'

1.3 非空約束

什麼是非空約束:某一列不能為 null。

-- 1.建立表學生表 test, 包含欄位(id,name,gender)其中 name 不能為 NULL
-- 格式:欄位名 欄位型別 NOT NULL
create table test (
    id int,
    name varchar(20) not null,
    gender char(1)
)
-- 新增一條記錄其中姓名不賦值
insert into test values (2,null,'男');-- 報錯:Column 'name' cannot be null

預設值

-- 建立一個學生表 test,包含欄位(id,name,address), 地址預設值是廣州
create table test2 (
 id int,
 name varchar(20),
 address varchar(20) default '廣州'
)
-- 新增一條記錄,使用預設地址
insert into test2 values (1, '李四', default);

1.4 外來鍵約束

(1)什麼是外來鍵約束?

我們先了解一下外來鍵的概念:

  • 什麼是外來鍵:在從表中與主表主鍵對應的那一列,如:員工表中的 dep_id
  • 主表: 一方,用來約束別人的表
  • 從表: 多方,被別人約束的表

image-20201118231045386

由上圖我們可以知道,外來鍵約束就是主表主鍵對從表外來鍵的約束,當從表插入資料時,外來鍵值必須在主表主鍵中存在,否則無法插入資料。

(2)建立外來鍵約束

-- 1.新建表時新增外來鍵約束
-- 格式:[CONSTRAINT] [外來鍵約束名稱] FOREIGN KEY(外來鍵欄位名) REFERENCES 主表名(主鍵欄位名)
-- 建立主表
create table department(
id int primary key auto_increment,
dep_name varchar(20),
dep_location varchar(20)
);
-- 建立從表,新增外來鍵約束
create table employee(
id int primary key auto_increment,
name varchar(20),
age int,
dep_id int, -- 外來鍵對應主表的主鍵
-- 建立外來鍵約束
constraint emp_depid_fk foreign key (dep_id) references department(id)
)
-- 插入不存在的部門
-- 報錯: Cannot add or update a child row: a foreign key constraint fails
INSERT INTO employee (NAME, age, dep_id) VALUES ('張三', 18, 6);
-- 2.已有表增加外來鍵:
-- 格式:ALTER TABLE 從表 ADD [CONSTRAINT] [外來鍵約束名稱] FOREIGN KEY (外來鍵欄位名) REFERENCES 主表(主鍵欄位名);
-- 2.1刪除 employee 表的 emp_depid_fk 外來鍵
alter table employee drop foreign key emp_depid_fk;
-- 2.2在 employee 表情存在的情況下新增外來鍵
ALTER TABLE employee ADD constraint emp_depid_fk foreign key (dep_id) references department(id)

(3)外來鍵的級聯

我們先來看一下新增外來鍵約束後主表的一些操作:

-- 1.要把部門表中的 id 值 2,改成 5,能不能直接更新呢?
-- 報錯:Cannot delete or update a parent row: a foreign key constraint fails
update department set id=5 where id=2;
-- 2.要刪除部門 id 等於 1 的部門, 能不能直接刪除呢?
-- 報錯:Cannot delete or update a parent
delete from department where id=1;

從上面的程式碼我們可以知道,在新增外來鍵約束後,如果從表中存在和主表主鍵值相等的外來鍵,那麼這個主鍵值和外來鍵相等的記錄就無法刪除和修改,這時就需要級聯操作了。

什麼是級聯操作?即: 在修改和刪除主表的主鍵時,同時更新或刪除副表的外來鍵值,稱為級聯操作

image-20201118232733171

給從表新增級聯更新和刪除:

-- 刪除 employee 表,重新建立 employee 表,新增級聯更新和級聯刪除
drop table employee;
create table employee(
id int primary key auto_increment,
name varchar(20),
age int,
dep_id int, -- 外來鍵對應主表的主鍵
-- 建立外來鍵約束
constraint emp_depid_fk foreign key (dep_id) references
 department(id) on update cascade on delete cascade
)

這時我們再對主表進行更新和刪除,就沒有報錯了:

-- 把部門表中 id 等於 1 的部門改成 id 等於 10
update department set id=10 where id=1;
-- 刪除部門號是 2 的部門
delete from department where id=2;

2. 表與表之間的關係

2.1 一對一關係

一對一(1:1) 在實際的開發中應用不多,因為一對一可以建立成一張表。

兩種建表原則:

image-20201118234525873

例如學生表和身份證表:

image-20201119085921800

2.2 一對多關係

一對多(1:n):例如員工和部門表,一個部門對應多個員工,多個員工對應一個部門。

image-20201119090036662

2.3 多對多關係

多對多(n:n):例如學生和課程表,一個學生可以選擇多門課程,一門課程可以被多個學生選擇。

image-20201119090320600

3. 資料庫正規化

好的資料庫設計對資料的儲存效能和後期的程式開發,都會產生重要的影響。建立科學的,規範的資料庫就需要滿足一些規則來優化資料的設計和儲存,這些規則就稱為正規化。

目前關係資料庫有六種正規化:第一正規化(1NF)、第二正規化(2NF)、第三正規化(3NF)、巴斯-科德正規化(BCNF)、 第四正規化(4NF)和第五正規化(5NF,又稱完美正規化)。 滿足最低要求的正規化是第一正規化(1NF)。在第一正規化的基礎上進一步滿足更多規範要求的稱為第二正規化(2NF), 其餘正規化以次類推。一般說來,資料庫只需滿足第三正規化(3NF)就行了。

3.1 第一正規化(1NF)

第一正規化是最基本的正規化。如果資料庫表中的所有欄位值都是不可分解的原子值,就說明該資料庫表滿足了第一正規化。

第一正規化的合理遵循需要根據系統的實際需求來定。比如某些資料庫系統中需要用到“地址”這個屬性,本來直接將“地址”屬性設計成一個數據庫表的欄位就行。但是如果系統經常會訪問“地址”屬性中的“城市”部分,那麼就非要將“地址”這個屬性重新拆分為省份、城市、詳細地址等多個部分進行儲存,這樣在對地址中某一部分操作的時候將非常方便。這樣設計才算滿足了資料庫的第一正規化,如下表所示。

image-20201119091632159

上表所示的使用者資訊遵循了第一正規化的要求,這樣在對使用者使用城市進行分類的時候就非常方便,也提高了資料庫的效能。

3.2 第二正規化(2NF)

第二正規化在第一正規化的基礎之上更進一層。第二正規化需要確保資料庫表中的每一列都和主鍵相關,而不能只與主鍵的某一部分相關(主要針對聯合主鍵而言)。也就是說在一個數據庫表中,一個表中只能儲存一種資料,不可以把多種資料儲存在同一張資料庫表中。

比如要設計一個訂單資訊表,因為訂單中可能會有多種商品,所以要將訂單編號和商品編號作為資料庫表的聯合主鍵,如下表所示。

image-20201119092112414

這樣就產生一個問題:這個表中是以訂單編號和商品編號作為聯合主鍵。這樣在該表中商品名稱、單位、商品價格等資訊不與該表的主鍵相關,而僅僅是與商品編號相關。所以在這裡違反了第二正規化的設計原則。

而如果把這個訂單資訊表進行拆分,把商品資訊分離到另一個表中,把訂單專案表也分離到另一個表中,就非常完美了。如下所示:

image-20201119092137413

這樣設計,在很大程度上減小了資料庫的冗餘。如果要獲取訂單的商品資訊,使用商品編號到商品資訊表中查詢即可。

3.3 第三正規化(3NF)

第三正規化需要確保資料表中的每一列資料都和主鍵直接相關,而不能間接相關。

比如在設計一個訂單資料表的時候,可以將客戶編號作為一個外來鍵和訂單表建立相應的關係。而不可以在訂單表中新增關於客戶其它資訊(比如姓名、所屬公司等)的欄位。如下面這兩個表所示的設計就是一個滿足第三正規化的資料庫表

image-20201119092325418

這樣在查詢訂單資訊的時候,就可以使用客戶編號來引用客戶資訊表中的記錄,也不必在訂單資訊表中多次輸入客戶資訊的內容,減小了資料冗餘。