MySQL學習筆記(5)——JOIN聯表查詢,自連接查詢,分頁和排序,子查詢與嵌套查詢

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;

連接查詢模型

demo

完整的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

簡單多表查詢推導2

此外還要求學號爲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對比 (圖源百度)

三種基本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:

INNER JOIN

LEFT JOIN:(會從左表中返回所有的值,即使在右表中配有匹配)

LEFT JOIN

RIGHT JOIN :會從右表中返回所有的值,即使在左表中配有匹配

RIGHT JOIN

只是在這個需要下使用內連接(INNER JOIN)是正確的,其他兩個只是展示其連接效果。

建議刷題鞏固:LeetCode-DataBase

on與where的區別

on條件是在生成臨時表時使用的條件,它不管on中的條件是否爲真,都會返回左邊表中的記錄。
where條件是在臨時表生成好後,再對臨時表進行過濾的條件。這時已經沒有left join的含義(必須返回左邊表的記錄)了,條件不爲真的就全部過濾掉

SQL中ON和WHERE的區別(轉)

對於JOIN參與的表的關聯操作,如果需要不滿足連接條件的行也在我們的查詢範圍內的話,我們就必需把連接條件放在ON後面,而不能放在WHERE後面,如果我們把連接條件放在了WHERE後面,那幺所有的LEFT,RIGHT,等這些操作將不起任何作用,對於這種情況,它的效果就完全等同於INNER連接。對於那些不影響選擇行的條件,放在ON或者WHERE後面就可以。
記住:所有的連接條件都必需要放在ON後面,不然前面的所有LEFT,和RIGHT關聯將作爲擺設,而不起任何作用。

除了INNER JOIN,LEFT JOIN,RIGHT JOIN之外還有其他幾種JOIN。

七種JOIN理論及其SQL (圖源百度):

7join

3.自連接查詢(瞭解)

核心⚡️ :將一張表拆成兩張一樣的表即可

SQL自連接查詢可以避免的嗎?我發現很多自連接查詢都可以拆分的,有什麼場景是一定需要使用自連接才能解決的嗎?

子查詢一般的應用場景: 數據爲樹型結構,如子菜單查詢父菜單, 如省市區這樣的,如多文件夾裏的文件關係等,都在一張表裏查詢

實例:有以下文件結構file下有c,java,Python.然後下面又有各自的文件。

file

在數據表中有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');

filedata

這裏使用子查詢 查詢父文件夾和子文件夾的關係:即 父文件 子文件

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;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章