跳到主要內容

關於 JOIN 耐心總結,學不會你打我系列

現在隨着各種數據庫框架的盛行,在提高效率的同時也讓我們忽略了很多底層的連接過程,這篇文章是對 SQL 連接過程梳理,並涉及到了現在常用的 SQL 標準。



其實標準就是在不同的時間,制定的一些寫法或規範。



從 SQL 標準說起


在編寫 SQL 語句前,需要先了解在不同版本的規範,因為隨着版本的變化,在具體編寫 SQL 時會有所不同。對於 SQL 來說,SQL92 和 SQL99 是最常見的兩個 SQL 標準,92 和 99 對應其提出的年份。除此之外,還存在 SQL86、SQL89、SQL2003、SQL2008、SQL2011,SQL2016等等。


但對我們來說,SQL92 和 SQL99 是最常用的兩個標準,主要學習這兩個就可以了。


為了演示方便,現在數據庫中加入如下三張表:


每個學生屬於一個班級,通過班級的人數來對應班級的類型。


-- ----------------------------
DROP TABLE IF EXISTS `Student`;
CREATE TABLE `Student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT '',
`birth` varchar(20) NOT NULL DEFAULT '',
`sex` varchar(10) NOT NULL DEFAULT '',
`class_id` int(11) NOT NULL COMMENT '班級ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of Student
-- ----------------------------
INSERT INTO `Student` VALUES ('1', '胡一', '1994.1.1', '男', '1');
INSERT INTO `Student` VALUES ('3', '王阿', '1992.1.1', '女', '1');
INSERT INTO `Student` VALUES ('5', '王琦', '1993.1.2', '男', '1');
INSERT INTO `Student` VALUES ('7', '劉偉', '1998.2.2', '女', '1');
INSERT INTO `Student` VALUES ('11', '張使', '1994.1.1', '男', '3');
INSERT INTO `Student` VALUES ('13', '王阿', '1992.1.1', '女', '3');
INSERT INTO `Student` VALUES ('15', '夏琪', '1993.1.2', '男', '3');
INSERT INTO `Student` VALUES ('17', '劉表', '1998.2.2', '女', '3');
INSERT INTO `Student` VALUES ('19', '諸葛', '1994.1.1', '男', '3');
INSERT INTO `Student` VALUES ('21', '王前', '1992.1.1', '女', '3');
INSERT INTO `Student` VALUES ('23', '王意識', '1993.1.2', '男', '3');
INSERT INTO `Student` VALUES ('25', '劉等待', '1998.2.2', '女', '3');
INSERT INTO `Student` VALUES ('27', '胡是一', '1994.1.1', '男', '5');
INSERT INTO `Student` VALUES ('29', '王阿請', '1992.1.1', '女', '5');
INSERT INTO `Student` VALUES ('31', '王消息', '1993.1.2', '男', '5');
INSERT INTO `Student` VALUES ('33', '劉全', '1998.2.2', '女', '5');
INSERT INTO `Student` VALUES ('35', '胡愛', '1994.1.1', '男', '5');
INSERT INTO `Student` VALUES ('37', '王表', '1992.1.1', '女', '5');
INSERT INTO `Student` VALUES ('39', '王華', '1993.1.2', '男', '5');
INSERT INTO `Student` VALUES ('41', '劉偉以', '1998.2.2', '女', '5');
INSERT INTO `Student` VALUES ('43', '胡一彪', '1994.1.1', '男', '5');
INSERT INTO `Student` VALUES ('45', '王阿符', '1992.1.1', '女', '5');
INSERT INTO `Student` VALUES ('47', '王琦刪', '1993.1.2', '男', '5');
INSERT INTO `Student` VALUES ('49', '劉達達', '1998.2.2', '女', '5');

-- ----------------------------
-- Table structure for `Class`
-- ----------------------------
DROP TABLE IF EXISTS `Class`;
CREATE TABLE `Class` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT '',
`number` int(11) NOT NULL DEFAULT '',
`class_type_id` int(11) NOT NULL COMMENT '班級類型ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of Class
-- ----------------------------
INSERT INTO `Class` VALUES ('1', '1年1班', 4, '1');
INSERT INTO `Class` VALUES ('3', '1年2班', 8, '3');
INSERT INTO `Class` VALUES ('5', '1年3班', 12, '5');

CREATE TABLE `ClassType`(
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) NOT NULL DEFAULT '',
`minimum_number` int(11) NOT NULL DEFAULT '' COMMENT '最少的班級人數',
`maximum_number` int(11) NOT NULL DEFAULT '' COMMENT '最多的班級人數',
PRIMARY KEY(`id`)
);
INSERT INTO `ClassType` VALUES ('1', '小班', '1', '4');
INSERT INTO `ClassType` VALUES ('3', '中班', '5', '8');
INSERT INTO `ClassType` VALUES ('5', '大班', '9', '12');

SQL92


笛卡爾積(交叉連接)


笛卡爾積是一個數學上的概念,表示如果存在 X,Y 兩個集合,則 X,Y 的笛卡爾積記為 X * Y. 表示由 X,Y 組成有序對的所有情況。


對應在 SQL 中,就是將兩張表中的每一行進行組合。而且在連接時,可以沒有任何限制,可將沒有關聯關係的任意表進行連接。


這裏拿學生表和班級表舉例,在學生表中我們插入了20名學生的數據,課程表中插入三個班級。則學生和班級的笛卡爾結果就是將兩表的每行數據一一組合,最後就是有 24 * 3 = 72 行的結果,如下圖所示。


並且需要知道的是,下面學習的外連接,自連接,等值連接等都是在笛卡爾積的基礎上篩選得到的。


對應的 SQL92 寫法為:


select * from Student, Class;


等值連接(內連接)


等值連接就是將兩張表中都存在的列進行連接,具體來說就是 where 後面通過 = 進行篩選。


比如查詢 Student 和其所屬 Class 信息的關係:


SELECT * FROM Student as s, Class as c where s.class_id = c.id;


非等值連接


非等值連接就是將等值連接中的等號換成其他的過濾條件。


比如這裏查詢每個班級的信息以及所屬的班級類別。


SELECT * FROM Class as c, ClassType t where c.number between t.minimum_number and maximum_number;


外連接


對於 SQL92 的外連接來說,在連接時會將兩張表分為主表和從表,主表显示所有的數據,從表显示匹配到的數據,沒有匹配到的則显示 None. 用 + 表示從表的位置。


左外連接:左表是主表,右表時從表。


SELECT * FROM Student as s , Class as c where s.class_id = c.id(+);

右外連接:左表是從表,右表時主表。


SELECT * FROM Class as c, Student as s  where c.id = s.class_id(+);

注意 SQL92 中並沒有全外連接。


自連接


自連接一般用於連接本身這張表,由於常見的 DBMS 都會對自連接做一些優化,所以一般在子查詢和自連接的情況下都使用自連接。


比如想要查詢比1年1班人數多的班級:


子查詢:


SELECT * FROM Class WHERE number > (SELECT number FROM Class WHERE name="1年1班"); 

自連接:


SELECT c2.* FROM Class c1, Class c2 WHERE c1.number < c2.number and c1.name = "1年1班"; 


SQL99


交叉連接


SELECT * FROM Student CROSS JOIN Class;

還可以對多張表進行交叉連接,比如連接 Student,Class,ClassType 三張表,結果為 24 * 3 * 3 = 216 條。


相當於嵌套了三層 for 循環。



自然連接


其實就是 SQL92 中的等值連接,只不過連接的對象是具有相同列名,並且值也相同的內容。


SELECT * FROM Student NATURAL JOIN CLASS;

SELECT * FROM Student as s, Class as c where s.id = c.id;

如果想用 NATURAL JOIN 時,建議為兩表設置相同的列名,比如 Student 表中的班級列為 class_id, 則在 Class 表中,id 也應改為 class_id. 這樣連接更合理一些。


如果大家嘗試,自然連接的話,會發現查出來的結果集為空,不要奇怪,下面說一下原因:



這是因為,NATURAL JOIN 會自動連接兩張表中相同的列名,而對於 Student 和 Class 兩張表來說,id 和 name 在這兩張表都是相同的,所以既滿足 id 又滿足 name 的行是不存在的。


相當於 SQL 變成了這樣


SELECT * FROM Student as s, Class as c where s.id = c.id and s.name = c.name;

ON 連接


ON 連接其實對了 SQL92 中的等值連接和非等值連接:


等值連接:


SELECT * FROM Student as s JOIN Class as c ON s.class_id = c.id;

or

SELECT * FROM Student as s INNER JOIN Class as c ON s.class_id = c.id;

非等值連接:


SELECT * FROM Class as c JOIN ClassType t ON c.number between t.minimum_number and maximum_number;

USING 連接


和 NATURAL JOIN 很像,可以手動指定具有相同列名的列進行連接:


SELECT * FROM Student JOIN Class USING(id);


這時就解決了之前列存在重名,無法連接的情況。


外連接


左外連接: 左表是主表,右表時從表。


SELECT * FROM Student as s LEFT JOIN Class as c on s.class_id = c.id;
OR
SELECT * FROM Student as s LEFT OUTER JOIN Class as c on s.class_id = c.id;

右外連接:左表是從表,右表時主表。


SELECT * FROM Student as s RIGHT JOIN Class as c on s.class_id = c.id;
OR
SELECT * FROM Student as s RIGHT OUTER JOIN Class as c on s.class_id = c.id;

全外連接: 左外連接 + 右外的連接的合集


SELECT * FROM Student as s FULL JOIN Class as c ON s.class_id = c.id; 


MySQL 中沒有全外連接的概念。



自連接:


SELECT c2.* FROM Class c1 JOIN Class c2 ON c1.number < c2.number and c1.name = "1年1班"; 

SQL92 和 SQL99 的對比



  1. SQL92 中的等值連接(內連接),非等值連接,自連接對應了 SQL99 的 ON 連接,用於篩選滿足連接條件的數據行。


  2. SQL92 的笛卡爾積連接,對應了 SQL99 的交叉連接。


  3. SQL92 中的外連接並不包含全外連接,而 SQL99 支持,並且將 SQL92 中 WHERE 換為 SQL99 的 ON. 這樣的好處可以更清晰的表達連接表的過程,更直觀。

    SELECT ...
    FROM table1
    JOIN table2 ON filter_condition
    JOIN table3 ON filter_condition

  4. SQL99 多了自然連接和 USING 連接的過程,兩者的區別是是否需要顯式的指定列名。



總結


我們知道,在 SQL 中,按照年份劃分了不同的標準,其中最為常用的是 SQL-92 和 SQL-99 兩個標準。


接着,對比了 92 和 99 兩者的不同,發現 99 的標準在連接時,更加符合邏輯並且更加直觀。


最後,上一張各種連接的示意圖, 方便梳理複習:



參考


各種連接的不同

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】



USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能



台北網頁設計公司這麼多該如何選擇?



※智慧手機時代的來臨,RWD網頁設計為架站首選



※評比南投搬家公司費用收費行情懶人包大公開



※回頭車貨運收費標準



Orignal From: 關於 JOIN 耐心總結,學不會你打我系列

留言

這個網誌中的熱門文章

Python 併發總結,多線程,多進程,異步IO

1 測量函數運行時間 import time def profile(func): def wrapper(*args, ** kwargs): import time start = time.time() func( *args, ** kwargs) end = time.time() print ' COST: {} ' .format(end - start) return wrapper @profile def fib(n): if n<= 2 : return 1 return fib(n-1) + fib(n-2 ) fib( 35 )   2 啟動多個線程,並等待完成   2.1 使用threading.enumerate() import threading for i in range(2 ): t = threading.Thread(target=fib, args=(35 ,)) t.start() main_thread = threading.currentThread() for t in threading.enumerate(): if t is main_thread: continue t.join()   2.2 先保存啟動的線程 threads = [] for i in range(5 ): t = Thread(target=foo, args= (i,)) threads.append(t) t.start() for t in threads: t.join()   3 使用信號量,限制同時能有幾個線程訪問臨界區 from threading import Semaphore import time sema = Semaphor...

韋伯連續劇終於更新 期待第一季順利完結

  地球天文學界的跳票大王詹姆斯·韋伯空間望遠鏡 (James Webb Space Telescope,縮寫為 JWST)自 1996 年以來斷斷續續不按劇本演出的連續劇終於讓焦慮的觀眾們又等到了一次更新:五層遮陽罩測試順利完成。 裝配完成的韋伯望遠鏡與好夥伴遮陽罩同框啦。Credit: NASA   嚴格的測試是任何空間任務順利成功的重中之重。遮陽罩,這個韋伯望遠鏡異常重要的親密夥伴,要是無法正常運轉的話,韋伯的這一季天文界連續劇說不準就要一直拖更了。   詹姆斯·韋伯空間望遠鏡是歷史上造出的最先進的空間望遠鏡。它不僅是一架紅外望遠鏡,還具有特別高的靈敏度。但想要達到辣么高的靈敏度來研究系外行星和遙遠的宇宙童年,韋伯童鞋必須非常"冷靜",體溫升高的話,靈敏度會大大折損。這個時候,遮陽罩就要大顯身手啦。   遮陽罩在韋伯的設計中至關重要。韋伯望遠鏡會被發射到拉格朗日 L2 點,運行軌道很高,遠離太陽、地球與月球。太陽是韋伯的主要熱量干擾的來源,其次是地球與月球。遮陽罩會有效阻斷來自這三大熱源的能量並保護韋伯維持在工作溫度正常運轉。這個工作溫度指的是零下 220 攝氏度(-370 華氏度;50 開爾文)。 上圖中我們可以看出,韋伯望遠鏡的配置大致可分為兩部分:紅色較熱的一面溫度為 85 攝氏度,藍色較冷的一面溫度達到零下 233 攝氏度。紅色的這部分中,儀器包括太陽能板、通信設備、計算機、以及轉向裝置。藍色部分的主要裝置包括鏡面、探測器、濾光片等。Credit: STSci.   遮陽罩的那一部分和望遠鏡的鏡面這部分可以產生非常極端的溫差。遮陽的這面溫度可以達到 110 攝氏度,足以煮熟雞蛋,而背陰處的部分溫度極低,足以凍結氧氣。   工程師們剛剛完成了五層遮陽罩的測試,按照韋伯在 L2 時的運行狀態安裝了遮陽罩。L2 距離地球約 160 萬公里。NASA 表示這些測試使用了航天器的自帶系統來展開遮陽罩,測試目前都已成功完成。韋伯望遠鏡遮陽罩負責人 James Cooper 介紹說這是遮陽罩"第一次在望遠鏡系統的电子設備的控制下展開。儘管這個任務非常艱巨,難度高,但測試順利完成,遮陽罩展開時的狀態非常驚艷"。   遮陽罩由五層 Kapton 製成。Kapton 是一種聚酰亞胺薄膜材料, 耐高溫絕...

LINE 發票管家「一鍵分享發票」新功能,聚餐AA更好算帳

» » LINE 發票管家「一鍵分享發票」新功能,聚餐AA更好算帳 消費明細好清楚,不怕算錯錢啦! by in , 讀取中... 之前介紹過的「LINE 發票管家」,除了能對統一發票、管理消費紀錄,現在還能分享消費明細給其他朋友囉!趕快來看看該如何分享吧!讓大家在聚餐過後,不用再截圖、拍照把消費明細記錄下來分享到 LINE 好友群組,只要用 LINE發票管家就可以隨時一鍵分享消費明細,包括店家、消費時間、消費項目、金額通通都有,聚餐 AA 也更好算帳。 LINE 發票管家「一鍵分享發票」新功能,聚餐AA更好算帳 朋友聚餐完,經常會先由其中一位買單再事後收帳,不過難免擔心忘記拍照紀錄下消費明細,導致算帳變得很麻煩。但是,現在只要用 LINE發票管家,不僅能將發票存入載具後直接匯入發票,進而更方便管理每月的消費情況,現在還能用它來分享消費明細到 LINE 聊天室,不需要截圖,整個流程超級簡單! ▲圖片來源: 當使用 LINE發票管家並且綁定載具後,只要日常消費有將發票存入載具就通通會自動匯入 LINE發票管家。如果想暸解自己近期的消費情況,也能在 LINE發票管家點選「發票明細」查詢所有消費紀錄。 *小提醒:存入手機條碼的發票,待財政部 1-2 日作業時間將發票資料匯入後, 打開單筆消費後分享到指定對象或群組。 像是朋友聚餐或其它購物消費,就能在 LINE發票管家查看每一筆消費的所有消費明細,每一項餐點的項目、價格通通一目了然。如果想將該筆消費內容分享給好友收帳,只要點選該筆消費明細後,接著點選畫面右上角的「分享」按鈕。 網頁設計 最專業,超強功能平台可客製,窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機,請問 台中電動車 哪裡在賣比較便宜可以到台中景泰電動車門市去看看總店:臺中市潭子區潭秀里雅潭路一段102-1號。 電動車補助 推薦評價好的 iphone維修 中心擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢住家的頂樓裝 太陽光電 聽說可發揮隔熱功效一線推薦東陽能源擁有核心技術、...