開心一刻html
閨蜜家暴富,買了一棟大別野,喊我去吃飯,菜挺豐盛的,筷子有些不給力,銀筷子,好重,我說換個竹子的,閨蜜說,這種銀筷子我家總共才五雙,只有貴賓才能用~我咬着牙享受着貴賓待遇,終於,在第三次夾蝦排滑落盤子時,我爆發了:去它喵的貴賓,我要蝦排……不是……我要竹筷子!android
簡單來講,就是將其餘表中的列添加過來,進行"添加列"的運算,以下圖所示。ios
爲何須要進行"添加列"的操做 了? 由於咱們在設計數據庫的時候,每每須要知足範式(具體知足範式幾,沒法一律而論,這裏不作細究),會致使咱們某個需求的所有列分散在不一樣的表中,因此爲了知足需求,咱們須要將某些表的列進行鏈接。咱們來看個簡單例子,假如咱們有兩張表(t_user,t_login_log):sql
DROP TABLE IF EXISTS t_user; CREATE TABLE t_user ( id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主鍵', user_name VARCHAR(50) NOT NULL COMMENT '用戶名', sex TINYINT(1) NOT NULL COMMENT '性別, 1:男,0:女', age TINYINT(3) UNSIGNED NOT NULL COMMENT '年齡', phone_number VARCHAR(11) NOT NULL DEFAULT '' COMMENT '電話號碼', email VARCHAR(50) NOT NULL DEFAULT '' COMMENT '電子郵箱', create_time datetime NOT NULL COMMENT '建立時間', update_time datetime NOT NULL COMMENT '更新時間', PRIMARY KEY (id) ) COMMENT='用戶表'; DROP TABLE IF EXISTS t_login_log; CREATE TABLE t_login_log ( id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主鍵', user_name VARCHAR(50) NOT NULL COMMENT '用戶名', ip VARCHAR(15) NOT NULL COMMENT '登陸IP', client TINYINT(1) NOT NULL COMMENT '登陸端, 1:android, 2:ios, 3:PC, 4:H5', create_time datetime NOT NULL COMMENT '建立時間', PRIMARY KEY (id) ) COMMENT='登陸日誌'; INSERT INTO t_user(user_name, sex, age, phone_number,email,create_time,update_time) VALUES ('Bruce Lee', 1, 32, '15174480987', 'brucelee@126.com', NOW(), NOW()), ('Jackie Chan', 1, 65, '15174481234', 'JackieChan@126.com', NOW(), NOW()), ('Jet Li', 1, 56, '15174481245', 'JetLi@126.com', NOW(), NOW()), ('Jack Ma', 1, 55, '15174481256', 'JackMa@126.com', NOW(), NOW()), ('Pony', 1, 48, '15174481278', 'Pony@126.com', NOW(), NOW()), ('Robin Li', 1, 51, '15174481290', 'RobinLi@126.com', NOW(), NOW()); INSERT INTO t_login_log(user_name, ip, client, create_time) VALUES ('Jackie Chan', '10.53.56.78',2, '2019-10-12 12:23:45'), ('Jackie Chan', '10.53.56.78',2, '2019-10-12 22:23:45'), ('Jet Li', '10.53.56.12',1, '2018-08-12 22:23:45'), ('Jet Li', '10.53.56.12',1, '2019-10-19 10:23:45'), ('Jack Ma', '198.11.132.198',2, '2018-05-12 22:23:45'), ('Jack Ma', '198.11.132.198',2, '2018-11-11 22:23:45'), ('Jack Ma', '198.11.132.198',2, '2019-06-18 22:23:45'), ('Robin Li', '220.181.38.148',3, '2019-10-21 09:45:56'), ('Robin Li', '220.181.38.148',3, '2019-10-26 22:23:45'), ('Pony', '104.69.160.60',4, '2019-10-12 10:23:45'), ('Pony', '104.69.160.60',4, '2019-10-15 20:23:45');
若是咱們須要展現以下列表(需求:展現用戶列表,並顯示其最近登陸時間、最近登陸 IP),那麼就須要 t_user 和 t_login_log 連表查了數據庫
鏈接的類型有不少種,細分以下圖ide
講交叉鏈接以前了,咱們先來看看笛卡爾積,假設咱們兩個集合,集合A={a, b},集合B={0, 1, 2},則A與B的笛卡爾積爲{(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)},表示爲AxB,也就是集合A中的任一元素與集合B的每一個元素組合後的新集合則爲A與B的笛卡爾積(AxB)。數學上的笛卡爾積反映到數據庫中就是交叉鏈接(CROSS JOIN),結合上述的案例以下:優化
SELECT * FROM t_user CROSS JOIN t_login_log; -- 與 CROSS JOIN 獲得的結果相同 -- 過期的寫法,不符合 SQL標準,能讀懂就好,不推薦使用 SELECT * FROM t_user, t_login_log;
t_user 中有 6 條記錄, t_login_log 中有 11 條記錄,t_user CROSS JOIN t_login_log 的結果是 66( 6 乘以 11) 條記錄spa
交叉鏈接就是對兩張表中的所有記錄進行交叉組合,所以其結果是兩張表的乘積,這也是爲何交叉鏈接沒法使用內鏈接或外鏈接中所使用的 ON 子句的緣由。交叉鏈接基本不會應用到實際業務之中,緣由有兩個,一是其結果沒有實用價值,二是結果行數太多,須要花費大量的運算時間和硬件資源。雖然說交叉鏈接的實際使用場景幾乎沒有,但仍是有它的理論價值的,交叉鏈接是其餘全部鏈接運算的基礎,內鏈接是交叉鏈接的一部分,其結果是交叉鏈接的一部分(子集),外鏈接有點特殊,其結果包含交叉鏈接以外的內容;更多詳情,咱們接着往下看。設計
只返回兩張表匹配的記錄,就叫內鏈接,直觀的表現就是關鍵字:INNER JOIN ... ON,ON 表示兩張錶鏈接所使用的列(鏈接鍵);而內鏈接中又屬等值鏈接最經常使用日誌
簡單點來講,就是鏈接鍵相等
-- 等值鏈接 SELECT * FROM t_user tu INNER JOIN t_login_log ttl ON tu.user_name = ttl.user_name; -- INNER JOIN 能夠簡寫成 JOIN SELECT * FROM t_user tu JOIN t_login_log ttl ON tu.user_name = ttl.user_name; -- 不加鏈接鍵, 結果與 CROSS JOIN 同樣 SELECT * FROM t_user tu INNER JOIN t_login_log ttl
等值鏈接的結果中,每一條記錄的鏈接鍵的列的值是想等的,如上圖中的 user_name 和 user_name1(爲了區別於第一個user_name,數據庫系統自動取的別名,咱們能夠顯示的指定)
鏈接鍵的比較謂詞除了 = 以外的全部狀況,好比 >、<、<>(!=);不等值鏈接使用場景比較少,反正我在實際工做中幾乎沒用到過
SELECT * FROM t_user tu INNER JOIN t_login_log ttl ON tu.user_name <> ttl.user_name; SELECT * FROM t_user tu INNER JOIN t_login_log ttl ON tu.user_name > ttl.user_name;
不須要指定鏈接條件,數據庫系統會自動用相同的字段做爲鏈接鍵,直觀的表現就是關鍵字:NATURAL JOIN,NATURAL LEFT JOIN、NATURAL RIGHT JOIN;
鏈接鍵不直觀,須要去看兩張表中相同的字段有哪些;對於天然鏈接,瞭解便可,不推薦使用,反正我工做這麼久,一次都沒用過。
外鏈接的使用方式與內鏈接同樣,也是經過 ON 使用鏈接鍵將兩張錶鏈接,從結果中獲取咱們想要的數據,可是返回的結果與內鏈接有區別,具體咱們往下看
返回匹配的記錄,以及左表多餘的記錄,關鍵字:LEFT JOIN(LEFT OUTER JOIN 的簡寫)
SELECT * FROM t_user tu LEFT OUTER JOIN t_login_log ttl ON tu.user_name = ttl.user_name; -- LEFT JOIN 是 LEFT OUTER JOIN 的簡寫 SELECT * FROM t_user tu LEFT JOIN t_login_log ttl ON tu.user_name = ttl.user_name;
上圖中,前 11 條記錄是匹配的記錄,而第 12 條是不匹配、左表的記錄
返回匹配的記錄,以及表 B 多餘的記錄,關鍵字:RIGHT JOIN(RIGHT OUTER JOIN 的簡寫)
SELECT * FROM t_login_log ttl RIGHT OUTER JOIN t_user tu ON tu.user_name = ttl.user_name; -- RIGHT JOIN 是 RIGHT OUTER JOIN 的簡寫 SELECT * FROM t_login_log ttl RIGHT JOIN t_user tu ON tu.user_name = ttl.user_name;
因爲咱們習慣了從左往右(閱讀方式、寫做方式),所以在實際項目中,基本上用的都是左鏈接
返回匹配的記錄,以及左表和右表各自的多餘記錄,關鍵字:FULL JOIN (FULL OUTER JOIN 的簡寫)
SELECT * FROM t_user tu FULL OUTER JOIN t_login_log ttl ON tu.user_name = ttl.user_name; -- FULL JOIN 是 FULL OUTER JOIN 的簡寫 SELECT * FROM t_user tu FULL JOIN t_login_log ttl ON tu.user_name = ttl.user_name;
注意:MySQL 不支持 全鏈接,咱們能夠經過 左鏈接、右鏈接以後,再 UNION 來實現全鏈接
一張表,本身鏈接本身,簡單點來理解就是,左表、右表是同一張表;鏈接方式能夠是內鏈接、也能夠是外鏈接
更多詳情你們能夠去看:項目上線後,談一下感觸比較深的一點:查詢優化
對於此需求,你們會如何來寫這個 SQL ? 也許你們很容易想到左鏈接,以下所示
SELECT * FROM t_user tu LEFT JOIN t_login_log ttl ON tu.user_name = ttl.user_name;
可結果以下:
顯示的是每一個用戶的全部登陸日誌,不是咱們想要的結果;緣由是 t_user 中的一條記錄在 t_login_log 對應的記錄有多種狀況:0 條對應、1 條對應、多條對應,那這個 SQL 要怎麼寫呢,方式有多種,不侷限於以下實現
-- 一、鏈接配合子查詢,注意 Bruce Lee 從未登錄過 SELECT tu.user_name, tu.sex,tu.age, tu.phone_number,tu.email,tll.create_time,tll.ip FROM t_user tu LEFT JOIN t_login_log tll ON tu.user_name = tll.user_name WHERE tll.id = (SELECT MAX(id) FROM t_login_log WHERE user_name = tu.user_name) OR tll.user_name IS NULL; -- 二、t_login_log分組統計出各個用戶的最近一次登陸信息後,再與 t_user 聯表 SELECT tu.user_name, tu.sex,tu.age, tu.phone_number,tu.email,tll.create_time,tll.ip FROM t_user tu LEFT JOIN ( SELECT tb.* FROM( SELECT user_name, MAX(id) id FROM t_login_log GROUP BY user_name ) ta LEFT JOIN t_login_log tb ON ta.id = tb.id ) tll ON tu.user_name = tll.user_name;
具體的實現還得結合具體的業務和需求來實現,那樣才能寫出高效的 SQL;另外結合執行計劃來創建合適的索引。總之,沒有一成不變的、通用的高效 SQL,結合具體的業務才能寫出最合適的 SQL。
一、鏈接的描述方式
經常使用的維恩圖,描述以下
維恩圖描述有他的優點,但它很差表示交叉鏈接,同時容易讓人誤解成 SQL 中的集合操做;這裏推薦另一種描述方式,我以爲描述的更準確
CROSS JOIN
經常使用 JOIN
上圖中,顏色表示匹配關係,顏色相同表示匹配。返回結果中,若是另外一張表沒有匹配的記錄,則用 null 填充, 在上圖中則表示爲空白。
二、鏈接中 ON 指定鏈接鍵,鏈接鍵能夠指定多個,而 WHERE 仍是平時的做用,用來指定過濾條件;不推薦將鏈接鍵放於 WHERE 後;
三、實際工做中,用的最多的是 左鏈接 和 等值鏈接,其餘的用的特別少
《SQL進階教程》