1. 程式人生 > 資料庫 >Mysql多表、外來鍵、和資料庫設計

Mysql多表、外來鍵、和資料庫設計

Mysql多表、外來鍵、和資料庫設計

多表

實際開發中,一個專案通常需要很多張表才能完成。

外來鍵

新增外來鍵約束

/*
	外來鍵約束
		作用:外來鍵約束可以讓兩表之間產生一個對應關係,從而保證主表資料完整性
	外來鍵
		指的是在從表中與主表的主鍵對應的欄位
	主表和從表
		主表 主鍵id所在表,一的一方
		從表 外來鍵欄位所在的表,多的一方
	新增外來鍵格式
		1.建立表的時候新增外來鍵
		create table 表名(
			欄位...
			[constraint] [外來鍵約束名] foreign key (外來鍵欄位名) references 主表(主鍵欄位)
		);
*/

-- 建立部門表
-- 一方,主表
CREATE TABLE department(
id INT PRIMARY KEY AUTO_INCREMENT, 
dep_name VARCHAR(30),
dep_location VARCHAR(30)
);

-- 建立員工表,新增外來鍵
CREATE TABLE employee(
	eid INT PRIMARY KEY AUTO_INCREMENT,
	ename VARCHAR(20),
	age INT,
	dept_id INT,	-- 外來鍵欄位 指向主表主鍵
	-- 新增外來鍵約束
	CONSTRAINT emp_dept_fk FOREIGN KEY(dept_id) REFERENCES department(id)
);

-- 新增2個部門
INSERT INTO department VALUES(NULL, '研發部','廣州'),(NULL, '銷售部', '深圳');
SELECT * FROM department;

-- 正常新增資料 (從表外來鍵 對應主表主鍵)
INSERT INTO employee (ename, age, dept_id) VALUES ('張百萬', 20, 1);
INSERT INTO employee (ename, age, dept_id) VALUES ('趙四', 21, 1);
INSERT INTO employee (ename, age, dept_id) VALUES ('廣坤', 20, 1);
INSERT INTO employee (ename, age, dept_id) VALUES ('小斌', 20, 2);
INSERT INTO employee (ename, age, dept_id) VALUES ('豔秋', 22, 2);
INSERT INTO employee (ename, age, dept_id) VALUES ('大玲子', 18, 2);
SELECT * FROM employee;
-- 插入一條有問題的資料 (部門id不存在)
-- Cannot add or update a child row: a foreign key constraint fails
INSERT INTO employee (ename, age, dept_id) VALUES ('錯誤', 18, 3);

-- 建立表之後新增外來鍵
-- 語法格式 ALTER TABLE 從表 ADD CONSTRAINT emp_dept_fk FOREIGN KEY(dept_id) REFERENCES department(id)
ALTER TABLE employee ADD CONSTRAINT emp_dept_fk FOREIGN KEY(dept_id) REFERENCES department(id);

外來鍵刪除

/*
	刪除外來鍵約束
		語法格式
			alter table 從表 drop foreign key 外來鍵約束名稱
*/

-- 刪除employee表外來鍵
ALTER TABLE employee DROP FOREIGN KEY emp_dept_fk;

/*
	外來鍵約束注意事項
		1.主表的外來鍵型別必須和主表型別保持一致
		2.新增資料時,應該先新增主表的資料
		3.刪除資料時,需先刪除從表資料
*/

級聯刪除

/*
	級聯刪除
		指的是在刪除主表的資料同時,可以刪除與之相關的從表中的資料
	語法格式
		on delete cascade
*/

-- 重新建立新增級聯操作
CREATE TABLE employee(
eid INT PRIMARY KEY AUTO_INCREMENT,
ename VARCHAR(20),
age INT,
dept_id INT,
CONSTRAINT emp_dept_fk FOREIGN KEY(dept_id) REFERENCES department(id)
-- 新增級聯刪除
ON DELETE CASCADE
);

多表關係設計

實際開發中,一個專案通常需要很多張表才能完成。例如:一個商城專案就需要分類表(category)、商品表(products)、訂單表(orders)等多張表。且這些表的資料之間存在一定的關係,接下來我們一起學習一下多表關係設計方面的知識

表與表之間的三種關係

一對多關係: 最常見的關係, 學生對班級,員工對部門
多對多關係: 學生與課程, 使用者與角色
一對一關係: 使用較少,因為一對一關係可以合成為一張表

一對多關係

例如:班級和學生,部門和員工,客戶和訂單,分類和商品

建表原則:在從表(多方)建立一個欄位,欄位作為外來鍵指向主表(一方)的主鍵

在這裡插入圖片描述

多對多關係

例如:老師和學生,學生和課程,使用者和角色

建表原則:需要建立第三張表,中間表中至少兩個欄位,這兩個欄位分別作為外來鍵指向各自一方的主鍵。

在這裡插入圖片描述

一對一

在實際的開發中應用不多.因為一對一可以建立成一張表

建表原則:外來鍵唯一 主表的主鍵和從表的外來鍵(唯一),形成主外來鍵關係,外來鍵唯一 UNIQUE

設計 省&市表

省和市之間的關係是 一對多關係,一個省包含多個市

在這裡插入圖片描述

#建立省表 (主表,注意: 一定要新增主鍵約束)
CREATE TABLE province(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
description VARCHAR(20)
);
#建立市表 (從表,注意: 外來鍵型別一定要與主表主鍵一致)
CREATE TABLE city(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
description VARCHAR(20),
pid INT,
-- 新增外來鍵約束
CONSTRAINT pro_city_fk FOREIGN KEY (pid) REFERENCES province(id)
);

在這裡插入圖片描述

設計 演員與角色表

演員與角色 是多對多關係, 一個演員可以飾演多個角色, 一個角色同樣可以被不同的演員扮演

在這裡插入圖片描述

#建立演員表
CREATE TABLE actor(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
#建立角色表
CREATE TABLE role(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
#建立中間表
CREATE TABLE actor_role(
-- 中間表自己的主鍵
id INT PRIMARY KEY AUTO_INCREMENT,
-- 指向actor 表的外來鍵
aid INT,
-- 指向role 表的外來鍵
rid INT
);
-- 為中間表的aid欄位,新增外來鍵約束 指向演員表的主鍵
ALTER TABLE actor_role ADD FOREIGN KEY(aid) REFERENCES actor(id);
-- 為中間表的rid欄位, 新增外來鍵約束 指向角色表的主鍵
ALTER TABLE actor_role ADD FOREIGN KEY(rid) REFERENCES role(id);

在這裡插入圖片描述

多表查詢

資料準備

-- 建立 db3_2 資料庫,指定編碼
CREATE DATABASE db3_2 CHARACTER SET utf8;

建立分類表與商品表

#分類表 (一方 主表)
CREATE TABLE category (
cid VARCHAR(32) PRIMARY KEY ,
cname VARCHAR(50)
);
#商品表 (多方 從表)
CREATE TABLE products(
pid VARCHAR(32) PRIMARY KEY ,
pname VARCHAR(50),
price INT,
flag VARCHAR(2),   		#是否上架標記為:1表示上架、0表示下架   
category_id VARCHAR(32),
-- 新增外來鍵約束
FOREIGN KEY (category_id) REFERENCES category (cid)
);
#分類資料
INSERT INTO category(cid,cname) VALUES('c001','家電');
INSERT INTO category(cid,cname) VALUES('c002','鞋服');
INSERT INTO category(cid,cname) VALUES('c003','化妝品');
INSERT INTO category(cid,cname) VALUES('c004','汽車');
#商品資料
INSERT INTO products(pid, pname,price,flag,category_id) VALUES('p001','小米電視機',5000,'1','c001');
INSERT INTO products(pid, pname,price,flag,category_id) VALUES('p002','格力空調',3000,'1','c001');
INSERT INTO products(pid, pname,price,flag,category_id) VALUES('p003','美的冰箱',4500,'1','c001');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p004','籃球鞋',800,'1','c002');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p005','運動褲',200,'1','c002');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p006','T恤',300,'1','c002');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p007','衝鋒衣',2000,'1','c002');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p008','神仙水',800,'1','c003');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p009','大寶',200,'1','c003');

笛卡爾積

在這裡插入圖片描述

/*
	多表查詢語法
		select 欄位名稱 from 表名
*/

-- 笛卡兒積
-- 多表查詢 交叉連線查詢
SELECT * FROM products,category;

內連線

/*
	內連線查詢
		特點 通過指定條件去匹配兩張表中內容,匹配不上不顯示
		
		隱式內連線
			語法格式 :select 欄位名... from 左表,右表 where 連線條件
		顯式內連線
			語法格式: select 欄位名... from 左表 join 右表 on 連線條件
*/

-- 查詢所有商品資訊和對應的分類資訊
SELECT * FROM products,category WHERE category_id = cid;

-- 查詢商品表的商品名稱 和 價格,以及商品的分類資訊
SELECT p.pname,p.price,c.cname FROM products p,category c WHERE p.category_id = c.cid;

-- 查詢 格力空調是屬於哪一分類下的商品
SELECT p.pname,c.cname FROM products p,category c WHERE p.category_id = c.cid AND p.pname = '格力空調';
SELECT p.pname,c.cname FROM products p,category c WHERE p.category_id = c.cid AND p.pid = 'p002';

--  查詢所有商品資訊和對應的分類資訊、
SELECT * FROM products p JOIN category c ON p.category_id = c.cid;

--  查詢鞋服分類下,價格大於500的商品名稱和價格
SELECT p.pname,p.price FROM products p JOIN category c ON p.category_id = c.cid AND p.price > 500 AND c.cname = '鞋服';

外連線

/*
	外連線
		左外連線
			語法格式 select 欄位名 from 左表 left join 右表 on 連線條件 
			特點
				左表為基準匹配右表中的資料,若能夠匹配則顯現,若匹配不上左表正常顯示,右表顯示為null
		右外連線
			語法格式 select 欄位名 from 左表 right join 右表 on 連線條件 
			特點
				右表為基準匹配右表中的資料,若能夠匹配則顯現,若匹配不上右表正常顯示,左表顯示為null
*/

-- 左外連線
SELECT * FROM products p LEFT JOIN category c ON p.category_id = c.cid 

-- 左外連線, 查詢每個分類下的商品個數
SELECT c.cname,COUNT(p.pid) '商品個數' FROM category c LEFT JOIN products p ON c.cid  = p.category_id GROUP BY c.cname;

-- 右外連線查詢
SELECT * FROM products p RIGHT JOIN category c ON p.`category_id` = c.`cid`;

三種連線比較

在這裡插入圖片描述

子查詢

/*
	子查詢
		一條select語句的結果,作為另一條select的一部分
	  特點
			子查詢放在小括號中
			子查詢作為父查詢的條件使用
		分類
			where 子查詢:將子查詢結果,作為父查詢的比較條件
			from 子查詢:將子查詢結果作為一張表使用
			exists 子查詢:查詢結果為單列多行情況下,可以將子查詢結果作為父查詢的in函式中的條件使用
*/
-- 查詢價格最高的商品資訊
-- 1.查詢出最高的價格
SELECT MAX(price) FROM products;
-- 2.根據最高價格查出商品資訊
SELECT * FROM products WHERE price = 5000;
-- 使用一條SQL完成
SELECT * FROM products WHERE price = (SELECT MAX(price) FROM products);


-- 子查詢做條件
-- 查詢化妝品分類下的 商品名稱 商品價格
SELECT p.pname,p.price FROM products p WHERE p.category_id = (SELECT c.cid FROM category c WHERE c.cname = '化妝品');
-- 查詢小於平均價格的商品資訊
SELECT * FROM products p WHERE p.price < (SELECT AVG(price) FROM products);


-- 子查詢作為一張表
-- 語法格式:SELECT 查詢欄位 FROM (子查詢)表別名 WHERE 條件;
-- 查詢商品中,價格大於500的商品資訊,包括 商品名稱 商品價格 商品所屬分類名稱
-- 注意:子查詢的結果作為一張表使用,一定要起一個別名,否則無法訪問表中欄位
SELECT p.pname,p.price,c.cname FROM products p INNER JOIN (SELECT * FROM category) c ON p.category_id = c.cid WHERE p.price > 500;


-- 子查詢結果是單列多行
-- 子查詢的結果類似一個數組, 父層查詢使用 IN 函式 ,包含子查詢的結果
-- 語法格式:SELECT 查詢欄位 FROM 表 WHERE 欄位 IN (子查詢);
-- 查詢價格小於兩千的商品,來自於哪些分類(名稱)
## 1.查詢小於兩千的商品的分類id
SELECT DISTINCT category_id FROM products WHERE price < 2000;
## 2.根據分類id查詢分類資訊
SELECT * FROM category WHERE cid IN (SELECT DISTINCT category_id FROM products WHERE price < 2000);

-- 查詢家電類 與 鞋服類下面的全部商品資訊
## 1.獲取家電類和鞋服類的分類id
SELECT cid FROM category WHERE cname IN ('家電','鞋服');
## 2.根據分類id查詢商品
SELECT * FROM products WHERE category_id IN (SELECT cid FROM category WHERE cname IN ('家電','鞋服'));


/*
	子查詢總結
		子查詢如果是一個欄位(單列),那麼就在where後面做條件
		如果是多個欄位(多列),就當作一張表使用(起別名)
*/

資料庫三正規化

概念: 三正規化就是設計資料庫的規則

規則

為了建立冗餘較小、結構合理的資料庫,設計資料庫時必須遵循一定的規則。在關係型資料庫中這種規則就稱為正規化。正規化是符合某一種設計要求的總結。要想設計一個結構合理的關係型資料庫,必須滿足一定的正規化
滿足最低要求的正規化是第一正規化(1NF)。在第一正規化的基礎上進一步滿足更多規範要求的稱為第二正規化(2NF) , 其餘正規化以此類推。一般說來,資料庫只需滿足第三正規化(3NF)就行了

第一正規化 1NF

原子性, 做到列不可拆分
第一正規化是最基本的正規化。資料庫表裡面欄位都是單一屬性的,不可再分, 如果資料表中每個欄位都是不可再分的最小資料單元,則滿足第一正規化

在這裡插入圖片描述

第二正規化 2NF

在第一正規化的基礎上更進一步,目標是確保表中的每列都和主鍵相關
一張表只能描述一件事

在這裡插入圖片描述

第三正規化 3NF

消除傳遞依賴
表的資訊,如果能夠被推匯出來,就不應該單獨的設計一個欄位來存放

在這裡插入圖片描述

資料庫反三正規化

反正規化化指的是通過增加冗餘或重複的資料來提高資料庫的讀效能
浪費儲存空間,節省查詢時間 (以空間換時間)

冗餘欄位

設計資料庫時,某一個欄位屬於一張表,但它同時出現在另一個或多個表,且完全等同於它在其本來所屬表的意義表示,那麼這個欄位就是一個冗餘欄位

總結

建立一個關係型資料庫設計,我們有兩種選擇
1,儘量遵循正規化理論的規約,儘可能少的冗餘欄位,讓資料庫設計看起來精緻、優雅、讓人心醉。
2,合理的加入冗餘欄位這個潤滑劑,減少join,讓資料庫執行效能更高更快。