MYSQL四
-- ########## 01、ER關系 ##########-- ER關系(邏輯描述)
-- A:E---Entity簡寫,實體,具有相同屬性(特征)的對象歸為同一實體
-- Attribute屬性,描述實體具有的特征,一個實體可以有若干個屬性來描述
-- B:R---Relationship簡寫,關系/聯系,數據對象彼此之間的互相關聯的關系-- ER關系常見的三種形式:
-- 1、一對一關系(1:1):例如:夫妻關系、一個國家只有一個最高領導人、向日葵...
-- 2、一對多關系(1:n):例如:一把鎖有多把鑰匙、一個房子有多個門、一個皇帝有多個太監、一個部門有多個員工...
-- 3、多對多關系(m:n):例如:參加招聘會的企業和同學、學生選課、飯桌上的菜和吃菜的人...-- ########## 02、數據庫設計 ##########
-- 設計的過程不是一步到位的,也不可能一步到位,都是一個反復迭代的過程-- 需求:商城里會員顧客購買若干數量的商品
-- 提供的數據如下:
-- 顧客編號為1的顧客張三購買了3個商品編號為123的A商品
-- 顧客編號為2的顧客李四購買了4個商品編號為456的B商品
-- 顧客編號為1的顧客張三購買了3個商品編號為123的A商品(數據來自不同渠道)
-- 顧客編號為3的顧客王五購買了5個商品編號為123的A商品
-- 顧客編號為1的顧客張三購買了2個商品編號為456的B商品
-- .....-- 分析:這可能就是業務提供方描述的一個場景,數據庫設計時,會從中挖掘出很多信息
-- 數據整理:去除無效的數據,比如:去除重復的數據(可能來自不同的渠道,但是其實描述的是相同的數據內容)-- 廣大開發人員在長期的數據庫設計過程中歸納整理了幾個稱為【范式】的東西
-- 【第1范式(1NF)】為了保證表中的行的數據是唯一的,屬性是原子的。
-- 也就是說,行的唯一性通過在表中定義一個唯一的主鍵來實現
-- 注意:這里說的唯一的主鍵,沒有說主鍵只能建立在一個字段上CREATE TABLE order_1nf
(orderid INT,productid INT,quantity INT,customerid INT,customername VARCHAR(10) NOT NULL,productname VARCHAR(20) NOT NULL,PRIMARY KEY(orderid, productid)
);
DESC order_1nf;-- 【第2范式(2NF)】首先數據必須滿足第1范式,其次要求非鍵屬性(Nonkey Attribute)和候選鍵之間必須滿足一定的條件。
-- 也就是說,一個非鍵屬性不能只完全函數依賴于候選鍵的一部分。
-- 分析:order_1nf表中,customerid這個非鍵屬性只依賴于主鍵(候選鍵)(orderid, productid)的一部分orderid,所以,需要把order_1nf表進行分解
CREATE TABLE order_2nf
(orderid INT,customerid INT,customername VARCHAR(10) NOT NULL,PRIMARY KEY(orderid)
);
CREATE TABLE orderdetail_2nf
(orderid INT,productid INT,quantity INT,PRIMARY KEY(orderid)
);
CREATE TABLE product_2nf
(productid INT,productname VARCHAR(20) NOT NULL,PRIMARY KEY(productid)
);-- 【第3范式(3NF)】首先數據必須滿足第2范式,其次要求所有的非鍵屬性必須非傳遞依賴于候選鍵。
-- 也就是說,一個非鍵屬性不能依賴于其他的非鍵屬性。
-- 分析:order_2nf表中,customername依賴于customerid這個非鍵屬性,所以,需要把order_2nf表進行分解
CREATE TABLE order_3nf
(orderid INT,customerid INT,PRIMARY KEY(orderid)
);
CREATE TABLE customer_3nf
(customerid INT,customername VARCHAR(10) NOT NULL,PRIMARY KEY(customerid)
);
CREATE TABLE orderdetail_3nf
(orderid INT,productid INT,quantity INT,PRIMARY KEY(orderid)
);
CREATE TABLE product_3nf
(productid INT,productname VARCHAR(20) NOT NULL,PRIMARY KEY(productid)
);-- 注意:實際開發中,一般必須達到第2范式及以上,不過有時處于操作數據效率的考慮,設計時也會使用第2范式的設計(即包含了一些冗余)-- 需求:設計 學生選課的數據庫設計
-- 學生可以選擇多門課程,一門課程可以有多個學生,一個老師可以帶多門課程,一門課程只能由一個老師來帶,每個學生選的課程有一個相應的課程成績
-- 分析:
-- 實體:學生、課程、老師、成績(這四個名詞拿出來作為候選實體)
-- 實體間的關系:"一個老師可以帶多門課程,一門課程只能由一個老師來帶"這句話描述了課程 和 老師 之間的關系是多對一的關系
-- "學生可以選擇多門課程,一門課程可以有多個學生"這句話描述了 學生 和 課程之間的關系是多對多的關系
-- "每個學生選的課程有一個相應的課程成績"這句話讀起來 成績 像是 課程的一個屬性,并且是一個學生的一門課程的一個成績,
-- 結合上述應該要先解決多對多的關系,再看這個成績怎么設計-- 首先設計較為獨立的實體
-- 老師信息實體
CREATE TABLE teacherinfo
(-- 老師編號teacherid INT AUTO_INCREMENT PRIMARY KEY,-- 老師姓名teachername VARCHAR(10) NOT NULL,-- 老師性別teachergender ENUM('男', '女') NOT NULL,-- 老師年齡teacherage INT NOT NULL
);
-- 學生信息實體
CREATE TABLE studentinfo
(-- 學生編號studentid INT AUTO_INCREMENT PRIMARY KEY,-- 學生姓名studentname VARCHAR(10) NOT NULL,-- 學生性別studentgender ENUM('男', '女') NOT NULL,-- 學生年齡studentage INT NOT NULL
);-- 接著設計一對多的關系,可以在作為多的實體中添加作為一的實體的主鍵作為非鍵屬性的值
-- 也可以在作為一的實體中添加作為多的實體的主鍵作為非鍵屬性的值
-- 顯然,前者操作起來比較簡單,也是一對多關系的經典設計方式-- 課程信息實體
CREATE TABLE courseinfo
(-- 課程編號courseid INT AUTO_INCREMENT PRIMARY KEY,-- 課程名稱coursename VARCHAR(10) NOT NULL,-- 老師編號teacherid INT NOT NULL
);-- 最后考慮多對多的關系,首先考慮套用一下一對多的關系的設計,在courseinfo表中放入studentinfo表的主鍵,在studentinfo表中放入courseinfo表的主鍵
-- 這樣的設計有一個問題,作為學生信息,并不確定有多少課程被選,即無法確定多個課程主鍵的數量,當然寫在一個字段中也可以,但是操作又很麻煩
-- 同樣,作為課程信息,也不確定有多少學生選擇
-- 既然考慮這兩個實體中都要放入對方的主鍵作為屬性使用,這里不妨制作一個獨立的關系實體,包含有著兩個實體的主鍵作為屬性,即可輕松實現多對多的關系
-- 而且,這個獨立的關系實體還可以包含有和多對多關系相關的其他屬性,比如:成績-- 成績信息實體(學生 和 課程 多對多 關聯關系表)
CREATE TABLE scoreinfo
(-- 成績編號scoreid INT AUTO_INCREMENT PRIMARY KEY,-- 學生編號studentid INT NOT NULL,-- 課程編號courseid INT NOT NULL,-- 成績分數score DECIMAL(4, 1) NOT NULL
);-- ########## 03、匯總數據(進階) ##########-- 模擬數據
INSERT INTO scoreinfo VALUES(NULL, 1, 1, 60), (NULL, 1, 2, 90), (NULL, 1, 3, 80), (NULL, 1, 4, 70), (NULL, 1, 5, 40),
(NULL, 2, 1, 70), (NULL, 2, 2, 50), (NULL, 2, 3, 70), (NULL, 2, 4, 30), (NULL, 2, 5, 90),
(NULL, 3, 1, 55), (NULL, 3, 2, 65), (NULL, 3, 3, 75),
(NULL, 4, 1, 87), (NULL, 4, 2, 63), (NULL, 4, 4, 28);SELECT * FROM scoreinfo;-- 需求:獲取每個同學的成績總分和成績平均分
-- 思路:因為每個同學可能有多門課程的成績,所以這里需要基于學生編號進行分組,并使用聚合函數
SELECT studentid AS 學生編號, SUM(score) AS 成績總分, AVG(score) AS 成績平均分
FROM scoreinfo GROUP BY studentid;-- 需求:列出每門功課的最高分、最低分,該門功課的總分、該門功課的平均分
-- 思路:因為每門功課可能對應多個學生的成績,所以這里需要基于課程編號進行分組,并使用聚合函數
SELECT courseid AS 課程編號, MAX(score) AS 最高分, MIN(score) AS 最低分, SUM(score) AS 總分, AVG(score) AS 平均分
FROM scoreinfo GROUP BY courseid;-- 需求:列出平均分高于65分的同學的學生編號及其平均分
-- 思路:首先想到的是使用分組子句(GROUP BY) 和 篩選條件子句(WHERE)-- 1、語義錯誤:先對scoreinfo這個集合進行了條件篩選,把65分以上的成績都留下來了,再按學生編號進行分組
SELECT studentid AS 學生編號, AVG(score) AS 平均分
FROM scoreinfo WHERE score > 65 GROUP BY studentid;-- 2、語法錯誤:參照順序圖,因為WHERE子句先于GROUP BY子句執行,所以在WHERE子句中無法提前使用聚合函數
-- 錯誤代碼: 1111 Invalid use of group function
SELECT studentid AS 學生編號, AVG(score) AS 平均分
FROM scoreinfo WHERE AVG(score) > 65 GROUP BY studentid;-- 正確寫法:引入 HAVING 子句,對于分組后的數據進行條件篩選
SELECT studentid AS 學生編號, AVG(score) AS 平均分
FROM scoreinfo
GROUP BY studentid
HAVING AVG(score) > 65;-- 注意:
-- 1、WHERE子句關注的是行的記錄的條件篩選
-- 2、HAVING子句關注的是組的記錄的條件篩選SELECT * FROM scoreinfo HAVING score > 70;
-- 上句可以理解為:
SELECT * FROM scoreinfo GROUP BY scoreid HAVING score > 70;
-- 下面兩句可以理解為:因為按照scoreid這個主鍵進行分組,每組就是一條記錄,對這一條記錄使用聚合函數還是它自身
SELECT * FROM scoreinfo GROUP BY scoreid HAVING AVG(score) > 70;
SELECT * FROM scoreinfo GROUP BY scoreid HAVING SUM(score) > 70;
-- 也就等價于
SELECT * FROM scoreinfo WHERE score > 70;-- 需求:列出單科課程(課程編號)對應的所有同學(多行顯示同學編號)
-- 如下寫法不正確,因為這樣分組后select獲取的是每一組的第一條記錄
-- 思考:為什么到處使用分組?
SELECT courseid AS 課程編號, studentid AS 學生編號
FROM scoreinfo
GROUP BY courseid;-- 正確寫法
SELECT courseid AS 課程編號, studentid AS 學生編號
FROM scoreinfo
ORDER BY courseid;
-- 或者
SELECT courseid AS 課程編號, studentid AS 學生編號
FROM scoreinfo
ORDER BY courseid, studentid;-- 需求:列出單科課程(課程編號)對應的所有同學(單行顯示同學編號,多個編號之間以逗號連接)
-- 思路:首先考慮分組,分組后select時使用字符函數CONCAT,但是這樣拼接的是每一組的第一條記錄中studentid
SELECT courseid AS 課程編號, CONCAT(studentid, ',') AS 學生編號
FROM scoreinfo
GROUP BY courseid;-- 到MySQL手冊中找靈感,查找了一下和GROUP BY子句相關的內容,看到GROUP_CONCAT(expr) :該函數返回帶有來自一個組的連接的非NULL值的字符串結果。
SELECT courseid AS 課程編號, GROUP_CONCAT(studentid) AS 學生編號
FROM scoreinfo
GROUP BY courseid;-- 需求:列出單科課程(課程編號)對應的學生人數
SELECT courseid AS 課程編號, COUNT(studentid) AS 選擇該門課程的學生人數
FROM scoreinfo
GROUP BY courseid;-- 需求:列出單科課程(課程編號)對應的學生人數,并進行總計
-- 思路:總計操作使用 WITH ROLLUP 子句
SELECT courseid AS 課程編號, COUNT(studentid) AS 選擇該門課程的學生人數
FROM scoreinfo
GROUP BY courseid
WITH ROLLUP;-- 需求:列出單科課程(課程編號)對應的所有學生(單行顯示學生編號),并進行總人數的統計(單行顯示學生編號)
SELECT courseid AS 課程編號, GROUP_CONCAT(studentid) AS 選擇該門課程的學生編號
FROM scoreinfo
GROUP BY courseid
WITH ROLLUP;
?
轉載于:https://www.cnblogs.com/java-le/p/6443404.html
總結
- 上一篇: Linux磁盘及文件系统(二)Linux
- 下一篇: 【模板】快速幂取模