MySQL學習筆記(5)——JOIN聯表查詢,自連接查詢,分頁和排序,子查詢與嵌套查詢
文章目錄
0.準備
參考:https://www.bilibili.com/video/BV1NJ411J79W?p=18
數據表
以下SQL案例均已該建表語句爲基礎:
major專業表 :majorid專業ID,majorname專業名稱,number專業人數
student學生表:id學號,name姓名,major專業名稱
-- ----------------------------
-- Table structure for `major`
-- ----------------------------
DROP TABLE IF EXISTS `major`;
CREATE TABLE `major` (
`majorid` int(8) unsigned NOT NULL AUTO_INCREMENT ,
`majorname` varchar(50) DEFAULT NULL,
`number` int(4) unsigned DEFAULT '0',
PRIMARY KEY (`majorid`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `student`
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(8) NOT NULL,
`name` varchar(50) DEFAULT NULL,
`major` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
完整的select語法規則
SELECT [ALL | DISTINCT]
{ * | table.* | [table.fieal1[as alias1][,table.field2[as aliias2]][,...] ]}
FROM table_name [as table_alias]
[left | right | inner JOIN table_name2] -- 聯合查詢
[WHERE ...] -- 指定where子句條件
[GROUP BY ...] --指定結果按照那幾個字段來分組
[HAVING] -- 過濾分組的記錄必須滿足的次要條件
[ORDER BY ...] -- 指定查詢記錄按照喲個或多個條件排序
[LIMIT{[offset,]row_count | row_countOFFSET offset}]; -- 指定查詢記錄從那條到那條(起始位置)
-- []代表可選 {}代表必選
順序:JOIN
, WHERE
, GROUP BY
, HAVING
, ORDER BY
, LIMIT
.必須嚴格按照順序,否則SQL執行會出錯誤。
可以看到 聯合查詢使用需要放在WHERE之前 。
1.簡單多表查詢
使用場景:需要查詢的子段源自多個表⚡️
例如:查詢學號爲1的學生姓名和專業名稱以及所屬專業的人數
學生姓名和專業名稱以及所屬專業的人數 屬於不同的表,來自student和major這兩張表。
以下針對該例子做sql的推導過程:
查詢學號爲1的學生姓名和專業名稱以及所屬專業的人數
首先明確要查詢的字段和來自那些表:
- 學生姓名(student表的name字段)
- 專業名稱(student表的majorname字段,major表的majorname字段)
- 所屬專業的人數(major表的number字段)
1.1 笛卡爾積
首先得明確兩表連接的結果爲笛卡爾積:
SELECT * FROM student,major;
1.2 從笛卡爾積種查詢所需要數據
兩表有聯繫得字段是專業名稱,因此可加個WHERE條件。
SELECT *
FROM student,major
WHERE student.majorname = major.majorname;
由於兩表字段名一樣,MYSQL自動爲後面那個majorname取別名爲majorname1
此外還要求學號爲1,只需要姓名,專業名,所屬專業人數:
-- 查詢學號爲1的學生姓名和專業名稱以及所屬專業的人數
SELECT student.`name`,student.majorname,major.number
FROM student,major
WHERE student.majorname = major.majorname AND id=1;
-- 使用AS爲表取別名 AS可以省略
SELECT s.id,s.`name`,s.majorname,m.number
FROM student AS s,major AS m
WHERE s.majorname = m.majorname AND id=1;
-- 使用AS取別名 省略AS
SELECT s.id,s.`name`,s.majorname,m.number
FROM student s,major m
WHERE s.majorname = m.majorname AND id=1;
FROM student , major 也可以寫成 FROM student INNER JOIN major 官方更推薦後者,具體差異可自行搜索。
SELECT s.id,s.`name`,s.majorname,m.number
FROM student s INNER JOIN major m;
WHERE s.majorname = m.majorname AND id=1;
-- INNER JOIN可簡寫爲JOIN
查詢結果都是以下圖片所示:
注意:
-
其中name字段使用 ``符號引起來,是爲了防止衝突,其實所有列名都可以用其引起來。
-
上述sql種其實只爲majorname標註是哪個表的就行(s.majorname),其他的字段不加也能查詢出結果,但是阿里巴巴Java開發規範中有如下建議:
**5.【強制】**對於數據庫中表記錄的查詢和變更,只要涉及多個表,都需要加表名(或別名)進行限定。
說明:多表join後作爲條件進行查詢記錄、更新記錄、刪除記錄時,如果出現沒有限定表名(或別名)的列名在多個表中均有存在,那麼會拋出異常。
正例:select t1.name from table_first as t1 , table_second as t2 where t1.id=t2.id;**反例:**在某業務中,由於多表關聯查詢語句沒有加表名(或別名)的限制,正常運行兩年後,最近在某個表中增加一個同名字段,在預發佈環境做數據庫變更後,線上查詢語句全部出現出1052異常:Column ‘name’ in field list is ambiguous,導致票務交易下跌。
2.JOIN 連接查詢(左中右)
三種基本JOIN對比 (圖源百度)
上圖中連接結果分別如下:
- 兩表
左聯接LEFTJOIN
結果爲 綠色部分 (左表內容除去與右表相同的部分) - 兩表
內聯接INEERJOIN
結果爲 紅色部分 (左表與右表相同的部分) - 兩表
右聯接INEERJOIN
結果爲 藍色部分 (右表內容除去與左表相同的部分)
操作 | 描述 |
---|---|
INNER JOIN | 如果表中至少有一個匹配,就返回行 |
LEFT JOIN | 會從左表中返回所有的值,即使在右表中配有匹配 |
RIGHT JOIN | 會從右表中返回所有的值,即使在左表中配有匹配 |
總之:以JOIN關鍵字前面的表爲標準
-- 例如:查詢學號爲1的學生姓名和專業名稱以及所屬專業的人數
SELECT s.id, s.`name`, s.majorname, m.number
FROM student s INNER JOIN major m
ON s.majorname = m.majorname AND id=1;
SELECT s.id, s.`name`, s.majorname, m.number
FROM student s LEFT JOIN major m
ON s.majorname = m.majorname AND id=1;
SELECT s.id, s.`name`, s.majorname, m.number
FROM student s RIGHT JOIN major m
ON s.majorname = m.majorname AND id=1;
同樣的例子,看看INNER JOIN,LEFT JOIN,RIGHT JOIN的區別:
INNER JOIN:
LEFT JOIN:(會從左表中返回所有的值,即使在右表中配有匹配)
RIGHT JOIN :會從右表中返回所有的值,即使在左表中配有匹配
只是在這個需要下使用內連接(INNER JOIN)是正確的,其他兩個只是展示其連接效果。
建議刷題鞏固:LeetCode-DataBase
on條件是在生成臨時表時使用的條件,它不管on中的條件是否爲真,都會返回左邊表中的記錄。
where條件是在臨時表生成好後,再對臨時表進行過濾的條件。這時已經沒有left join的含義(必須返回左邊表的記錄)了,條件不爲真的就全部過濾掉對於JOIN參與的表的關聯操作,如果需要不滿足連接條件的行也在我們的查詢範圍內的話,我們就必需把連接條件放在ON後面,而不能放在WHERE後面,如果我們把連接條件放在了WHERE後面,那幺所有的LEFT,RIGHT,等這些操作將不起任何作用,對於這種情況,它的效果就完全等同於INNER連接。對於那些不影響選擇行的條件,放在ON或者WHERE後面就可以。
記住:所有的連接條件都必需要放在ON後面,不然前面的所有LEFT,和RIGHT關聯將作爲擺設,而不起任何作用。
除了INNER JOIN,LEFT JOIN,RIGHT JOIN之外還有其他幾種JOIN。
七種JOIN理論及其SQL (圖源百度):
3.自連接查詢(瞭解)
核心⚡️ :將一張表拆成兩張一樣的表即可
SQL自連接查詢可以避免的嗎?我發現很多自連接查詢都可以拆分的,有什麼場景是一定需要使用自連接才能解決的嗎?
子查詢一般的應用場景: 數據爲樹型結構,如子菜單查詢父菜單, 如省市區這樣的,如多文件夾裏的文件關係等,都在一張表裏查詢
實例:有以下文件結構file下有c,java,Python.然後下面又有各自的文件。
在數據表中有id,parent_id,name三個字段file的id規定爲1,且在表中省略了file,建表語句和數據具體如下:
-- ----------------------------
-- Table structure for `file`
-- ----------------------------
DROP TABLE IF EXISTS `file`;
CREATE TABLE `file` (
`id` int(8) NOT NULL,
`parent_id` int(8) DEFAULT NULL,
`name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of file
-- ----------------------------
INSERT INTO `file` VALUES ('2', '1', 'Java');
INSERT INTO `file` VALUES ('3', '1', 'Python');
INSERT INTO `file` VALUES ('4', '1', 'C');
INSERT INTO `file` VALUES ('5', '2', 'Spring');
INSERT INTO `file` VALUES ('6', '2', 'SpringMVC');
INSERT INTO `file` VALUES ('7', '3', 'Flask');
INSERT INTO `file` VALUES ('8', '3', 'TensorFlow');
INSERT INTO `file` VALUES ('9', '4', 'Tinyhttpd');
INSERT INTO `file` VALUES ('10', '4', 'CMockery');
這裏使用子查詢 查詢父文件夾和子文件夾的關係:即 父文件 子文件
SELECT f1.name,f2.name FROM file f1,file f2 WHERE f1.id=f2.parent_id;
查詢結果如下:
4.分頁和排序
更具 完整的select語法規則 可知順序:JOIN
, WHERE
, GROUP BY
, HAVING
, ORDER BY
, LIMIT
.
-
分頁 limit
-
排序 order by
4.1 分頁
分頁目的:緩解數據庫壓力(數據庫),提升用戶體驗(前端顯示)
- LIMIT 起始值,每頁的大小
-- 分頁 每頁3個
SELECT majorname,number FROM major LIMIT 2; -- 前兩個條數據
SELECT majorname,number FROM major LIMIT 0,3; -- 第一頁 第一1到第三3條數據
SELECT majorname,number FROM major LIMIT 3,3; -- 第二頁 第一4到第一6條數據
SELECT majorname,number FROM major LIMIT 6,3; -- 第三頁 第一7到第一9條數據
- c參數:n:當前頁
- pagesize:頁面大小
- (n-1)*pageSize:起始值
- 總頁數=數據總數/頁面大小
公式:SELECT 字段 FROM 表 ... LIMIT (n-1)*pageSize,pageSize
4.2 排序
- 升序 ORDER BY 字段 ASC
- 降序 ORDER BY 字段 DESC
-- 按照專業人數個數升序(降序)查找專業名稱,專業人數
SELECT majorname,number FROM major ORDER BY number ASC; -- 升序
SELECT majorname,number FROM major ORDER BY number DESC;-- 降序
4.3 分頁排序組合應用
某一字段最大值:ORDER BY 字段 DESC LIMIT 1; 就是降序,頁面大小爲1
某一字段最小值:ORDER BY 字段 ASC LIMIT 1 ; 就是升序,頁面大小爲1
某一字段第二大的值 : ORDER BY 字段 DESC LIMIT 1,1; 就是降序分頁,從1開始(默認是0),頁面大小爲1
第二小同理。
5.子查詢與嵌套查詢
子查詢本質:WHERE子句中嵌套一個子查詢的語句
子查詢就是 括號中的查詢
子查詢與嵌套查詢:雖不是一個概念,但是相關的概念
-
嵌套查詢
是包括外層主查詢和內層子查詢的查詢。
子查詢還可以包括子查版詢,可以是多層子查詢,主查詢也可以包括多個子查詢。 -
子查詢
就是括號中的查詢。
看出來了吧:子查詢是嵌套查詢必不可少的組成部份。
例如下面的SQL例子中:括號內的SLELECT語句是子查詢,包含子查詢的整條SELLECT語句是嵌套查詢。
-- 使用子查詢
-- 例如:查詢專業人數最多的專業下的學號從小到大排在前2的學號,姓名,專業名稱
-- 拆成兩步:1.查詢專業人數最多的專業 2.該專業下的學號從小到大排在前2的學號,姓名,專業名稱
-- 1.查詢專業人數最多的專業 按照number降序 取一條數據 所以是number最大的那條數據
SELECT majorname FROM major order by number DESC limit 1;
-- 2.該專業下的學號從小到大排在前2的學號,姓名,專業名稱
-- 按照id升序 前兩條就是從小到大前二學號的數據了,在選擇學號,姓名,專業名稱這三個字段即可
SELECT id,`name`,majorname
FROM student
WHERE majorname=(SELECT majorname FROM major ORDER BY major.number DESC LIMIT 1)
ORDER BY id ASC LIMIT 2;