MySQL多表聯(lián)合查詢(xún)語(yǔ)句的編寫(xiě)及效率分析、優(yōu)化

一、多表連接類(lèi)型


1. 笛卡爾積(交叉連接)
在MySQL中可以為CROSS JOIN或者省略CROSS即JOIN婉徘,或者使用',' 如:

SELECT * FROM table1 CROSS JOIN table2   
SELECT * FROM table1 JOIN table2   
SELECT * FROM table1,table2  
SELECT * FROM users CROSS JOIN articles;
SELECT * FROM users JOIN articles;
SELECT * FROM users, articles;

由于其返回的結(jié)果為被連接的兩個(gè)數(shù)據(jù)表的乘積,因此當(dāng)有WHERE, ON或USING條件的時(shí)候一般不建議使用咐汞,因?yàn)楫?dāng)數(shù)據(jù)表項(xiàng)目太多的時(shí)候盖呼,會(huì)非常慢。一般使用LEFT [OUTER] JOIN或者RIGHT [OUTER] JOIN

2. 內(nèi)連接INNER JOIN
在MySQL中把INNER JOIN叫做等值連接化撕,即需要指定等值連接條件在MySQL中CROSS和INNER JOIN被劃分在一起塌计。

SELECT * FROM users as u INNER JOIN articles as a where u.id = a.user_id 

3. MySQL中的外連接
分為左外連接和右連接,即除了返回符合連接條件的結(jié)果之外侯谁,還要返回左表(左連接)或者右表(右連接)中不符合連接條件的結(jié)果锌仅,相對(duì)應(yīng)的使用NULL對(duì)應(yīng)。

例子:
users表:

+----+----------+----------+--------------------+
| id | username | password | email              |
+----+----------+----------+--------------------+
|  1 | junxi    | 123      | xinlei3166@126.com |
|  2 | tangtang | 456      | xinlei3166@126.com |
|  3 | ceshi3   | 456      | ceshi3@11.com      |
|  4 | ceshi4   | 456      | ceshi4@qq.com      |
|  5 | ceshi3   | 456      | ceshi3@11.com      |
|  6 | ceshi4   | 456      | ceshi4@qq.com      |
|  7 | ceshi3   | 456      | ceshi3@11.com      |
|  8 | ceshi4   | 456      | ceshi4@qq.com      |
|  9 | ceshi3   | 333      | ceshi3@11.com      |
| 10 | ceshi4   | 444      | ceshi4@qq.com      |
| 11 | ceshi3   | 333      | ceshi3@11.com      |
| 12 | ceshi4   | 444      | ceshi4@qq.com      |
+----+----------+----------+--------------------+

userinfos表:

+----+-------+--------+-------------+----------------+---------+
| id | name  | qq     | phone       | link           | user_id |
+----+-------+--------+-------------+----------------+---------+
|  1 | 君惜  | 666666 | 16616555188 | www.junxi.site |       1 |
|  2 | 糖糖  | 777777 | 17717555177 | www.weizhi.com |       2 |
|  3 | 測(cè)試3 | 333333 | 13313333177 | www.ceshi3.com |       3 |
+----+-------+--------+-------------+----------------+---------+

SQL語(yǔ)句:

SELECT * FROM users as u LEFT JOIN userinfos as i on u.id = i.user_id;

執(zhí)行結(jié)果:

+----+----------+----------+--------------------+------+-------+--------+-------------+----------------+---------+
| id | username | password | email              | id   | name  | qq     | phone       | link           | user_id |
+----+----------+----------+--------------------+------+-------+--------+-------------+----------------+---------+
|  1 | junxi    | 123      | xinlei3166@126.com |    1 | 君惜  | 666666 | 16616555188 | www.junxi.site |       1 |
|  2 | tangtang | 456      | xinlei3166@126.com |    2 | 糖糖  | 777777 | 17717555177 | www.weizhi.com |       2 |
|  3 | ceshi3   | 456      | ceshi3@11.com      |    3 | 測(cè)試3 | 333333 | 13313333177 | www.ceshi3.com |       3 |
|  4 | ceshi4   | 456      | ceshi4@qq.com      | NULL | NULL  | NULL   | NULL        | NULL           |    NULL |
|  5 | ceshi3   | 456      | ceshi3@11.com      | NULL | NULL  | NULL   | NULL        | NULL           |    NULL |
|  6 | ceshi4   | 456      | ceshi4@qq.com      | NULL | NULL  | NULL   | NULL        | NULL           |    NULL |
|  7 | ceshi3   | 456      | ceshi3@11.com      | NULL | NULL  | NULL   | NULL        | NULL           |    NULL |
|  8 | ceshi4   | 456      | ceshi4@qq.com      | NULL | NULL  | NULL   | NULL        | NULL           |    NULL |
|  9 | ceshi3   | 333      | ceshi3@11.com      | NULL | NULL  | NULL   | NULL        | NULL           |    NULL |
| 10 | ceshi4   | 444      | ceshi4@qq.com      | NULL | NULL  | NULL   | NULL        | NULL           |    NULL |
| 11 | ceshi3   | 333      | ceshi3@11.com      | NULL | NULL  | NULL   | NULL        | NULL           |    NULL |
| 12 | ceshi4   | 444      | ceshi4@qq.com      | NULL | NULL  | NULL   | NULL        | NULL           |    NULL |
+----+----------+----------+--------------------+------+-------+--------+-------------+----------------+---------+

分析:
而users表中的id大于3的用戶(hù)在userinfos中沒(méi)有相應(yīng)的紀(jì)錄墙贱,但是卻出現(xiàn)在了結(jié)果集中
因?yàn)楝F(xiàn)在是left join热芹,所有的工作以left為準(zhǔn).
結(jié)果1,2惨撇,3都是既在左表又在右表的紀(jì)錄4, 5, 6, 7, 8, 9, 10, 11, 12是只在左表伊脓,不在右表的紀(jì)錄

工作原理:
從左表讀出一條,選出所有與on匹配的右表紀(jì)錄(n條)進(jìn)行連接魁衙,形成n條紀(jì)錄(包括重復(fù)的行)报腔,如果右邊沒(méi)有與on條件匹配的表,那連接的字段都是null.然后繼續(xù)讀下一條剖淀。
引申:
我們可以用右表沒(méi)有on匹配則顯示null的規(guī)律, 來(lái)找出所有在左表纯蛾,不在右表的紀(jì)錄, 注意用來(lái)判斷的那列必須聲明為not null的纵隔。
如:
SQL:
(注意:
1.列值為null應(yīng)該用is null 而不能用=NULL
2.這里i.user_id 列必須聲明為 NOT NULL 的.

SELECT * FROM users as u LEFT JOIN userinfos as i on u.id = i.user_id WHERE i.user_id is NULL;

執(zhí)行結(jié)果:

+----+----------+----------+---------------+------+------+------+-------+------+---------+
| id | username | password | email         | id   | name | qq   | phone | link | user_id |
+----+----------+----------+---------------+------+------+------+-------+------+---------+
|  4 | ceshi4   | 456      | ceshi4@qq.com | NULL | NULL | NULL | NULL  | NULL |    NULL |
|  5 | ceshi3   | 456      | ceshi3@11.com | NULL | NULL | NULL | NULL  | NULL |    NULL |
|  6 | ceshi4   | 456      | ceshi4@qq.com | NULL | NULL | NULL | NULL  | NULL |    NULL |
|  7 | ceshi3   | 456      | ceshi3@11.com | NULL | NULL | NULL | NULL  | NULL |    NULL |
|  8 | ceshi4   | 456      | ceshi4@qq.com | NULL | NULL | NULL | NULL  | NULL |    NULL |
|  9 | ceshi3   | 333      | ceshi3@11.com | NULL | NULL | NULL | NULL  | NULL |    NULL |
| 10 | ceshi4   | 444      | ceshi4@qq.com | NULL | NULL | NULL | NULL  | NULL |    NULL |
| 11 | ceshi3   | 333      | ceshi3@11.com | NULL | NULL | NULL | NULL  | NULL |    NULL |
| 12 | ceshi4   | 444      | ceshi4@qq.com | NULL | NULL | NULL | NULL  | NULL |    NULL |
+----+----------+----------+---------------+------+------+------+-------+------+---------+

一般用法:
a. LEFT [OUTER] JOIN:
除了返回符合連接條件的結(jié)果之外翻诉,還需要顯示左表中不符合連接條件的數(shù)據(jù)列炮姨,相對(duì)應(yīng)使用NULL對(duì)應(yīng)

SELECT column_name FROM table1 LEFT [OUTER] JOIN table2 ON table1.column=table2.column 

b. RIGHT [OUTER] JOIN:
RIGHT與LEFT JOIN相似不同的僅僅是除了顯示符合連接條件的結(jié)果之外,還需要顯示右表中不符合連接條件的數(shù)據(jù)列碰煌,相應(yīng)使用NULL對(duì)應(yīng)

SELECT column_name FROM table1 RIGHT [OUTER] JOIN table2 ON table1.column=table2.column  

Tips:

  1. on a.c1 = b.c1 等同于 using(c1)
  2. INNER JOIN 和 , (逗號(hào)) 在語(yǔ)義上是等同的
  3. 當(dāng) MySQL 在從一個(gè)表中檢索信息時(shí)舒岸,你可以提示它選擇了哪一個(gè)索引。
    如果 EXPLAIN 顯示 MySQL 使用了可能的索引列表中錯(cuò)誤的索引芦圾,這個(gè)特性將是很有用的蛾派。
    通過(guò)指定 USE INDEX (key_list),你可以告訴 MySQL 使用可能的索引中最合適的一個(gè)索引在表中查找記錄行个少。
    可選的二選一句法 IGNORE INDEX (key_list) 可被用于告訴 MySQL 不使用特定的索引洪乍。如:
mysql> SELECT * FROM table1 USE INDEX (key1,key2) WHERE key1=1 AND key2=2 AND key3=3;  
mysql> SELECT * FROM table1 IGNORE INDEX (key3) WHERE key1=1 AND key2=2 AND key3=3;  
二、表連接的約束條件

添加顯示條件WHERE, ON, USING

1. WHERE子句

SELECT * FROM table1,table2 WHERE table1.id=table2.id;  
SELECT * FROM users, userinfos WHERE users.id=userinfos.user_id;

2. ON

SELECT * FROM table1 LEFT JOIN table2 ON table1.id=table2.id;  
SELECT * FROM table1 LEFT JOIN table2 ON table1.id=table2.id LEFT JOIN table3 ON table2.id=table3.id;  
SELECT * FROM users LEFT JOIN articles ON users.id = articles.user_id;
SELECT * FROM users LEFT JOIN userinfos ON users.id = userinfos.user_id LEFT JOIN articles ON users.id = articles.user_id;

3. USING子句稍算,如果連接的兩個(gè)表連接條件的兩個(gè)列具有相同的名字的話可以使用USING

例如:

SELECT FROM LEFT JOIN USING ()

連接多于兩個(gè)表的情況舉例:

SELECT users.username, userinfos.name, articles.title FROM users LEFT JOIN userinfos ON users.id = userinfos.user_id LEFT JOIN articles ON users.id = articles.user_id;

執(zhí)行結(jié)果:

mysql> SELECT users.username, userinfos.name, articles.title FROM users LEFT JOIN userinfos ON users.id = userinfos.user_id LEFT JOIN articles ON users.id = articles.user_id;
+----------+-------+------------+
| username | name  | title      |
+----------+-------+------------+
| junxi    | 君惜  | 中國(guó)有嘻哈 |
| tangtang | 糖糖  | 星光大道   |
| ceshi3   | 測(cè)試3 | 平凡的真諦 |
| junxi    | 君惜  | python進(jìn)階 |
| ceshi3   | NULL  | NULL       |
| ceshi3   | NULL  | NULL       |

或者

SELECT users.username, userinfos.name, articles.title FROM users LEFT JOIN userinfos ON users.id = userinfos.user_id LEFT JOIN articles ON users.id = articles.user_id WHERE (articles.user_id IS NOT NULL AND userinfos.user_id IS NOT NULL);

執(zhí)行結(jié)果:

mysql> SELECT users.username, userinfos.name, articles.title FROM users LEFT JOIN userinfos ON users.id = userinfos.user_id LEFT JOIN articles ON users.id = articles.user_id WHERE (articles.user_id IS NOT NULL AND userinfos.user_
id IS NOT NULL);
+----------+-------+------------+
| username | name  | title      |
+----------+-------+------------+
| junxi    | 君惜  | 中國(guó)有嘻哈 |
| tangtang | 糖糖  | 星光大道   |
| ceshi3   | 測(cè)試3 | 平凡的真諦 |
| junxi    | 君惜  | python進(jìn)階 |
+----------+-------+------------+

或者

SELECT users.username, userinfos.name, articles.title FROM users LEFT JOIN userinfos ON users.id = userinfos.user_id LEFT JOIN articles ON users.id = articles.user_id WHERE (userinfos.name = '君惜');

執(zhí)行結(jié)果:

mysql> SELECT users.username, userinfos.name, articles.title FROM users LEFT JOIN userinfos ON users.id = userinfos.user_id LEFT JOIN articles ON users.id = articles.user_id WHERE (userinfos.name = '君惜');
+----------+------+------------+
| username | name | title      |
+----------+------+------------+
| junxi    | 君惜 | 中國(guó)有嘻哈 |
| junxi    | 君惜 | python進(jìn)階 |
+----------+------+------------+

另外需要注意的地方 在MySQL中涉及到多表查詢(xún)的時(shí)候典尾,需要根據(jù)查詢(xún)的情況役拴,想好使用哪種連接方式效率更高糊探。

  1. 交叉連接(笛卡爾積)或者內(nèi)連接 [INNER | CROSS] JOIN
  2. 左外連接LEFT [OUTER] JOIN或者右外連接RIGHT [OUTER] JOIN 注意指定連接條件WHERE, ON,USING.
三河闰、MySQL如何優(yōu)化LEFT JOIN和RIGHT JOIN

在MySQL中科平,A LEFT JOIN B join_condition執(zhí)行過(guò)程如下:
1)· 根據(jù)表A和A依賴(lài)的所有表設(shè)置表B。
2)· 根據(jù)LEFT JOIN條件中使用的所有表(除了B)設(shè)置表A姜性。
3)· LEFT JOIN條件用于確定如何從表B搜索行瞪慧。(換句話說(shuō),不使用WHERE子句中的任何條件)部念。
4)· 可以對(duì)所有標(biāo)準(zhǔn)連接進(jìn)行優(yōu)化弃酌,只是只有從它所依賴(lài)的所有表讀取的表例外。如果出現(xiàn)循環(huán)依賴(lài)關(guān)系儡炼,MySQL提示出現(xiàn)一個(gè)錯(cuò)誤妓湘。
5)· 進(jìn)行所有標(biāo)準(zhǔn)WHERE優(yōu)化。
6)· 如果A中有一行匹配WHERE子句乌询,但B中沒(méi)有一行匹配ON條件榜贴,則生成另一個(gè)B行,其中所有列設(shè)置為NULL妹田。
7)· 如果使用LEFT JOIN找出在某些表中不存在的行唬党,并且進(jìn)行了下面的測(cè)試:WHERE部分的col_name IS NULL,其中col_name是一個(gè)聲明為 NOT NULL的列鬼佣,MySQL找到匹配LEFT JOIN條件的一個(gè)行后停止(為具體的關(guān)鍵字組合)搜索其它行驶拱。
RIGHT JOIN的執(zhí)行類(lèi)似LEFT JOIN,只是表的角色反過(guò)來(lái)晶衷。

連接優(yōu)化器計(jì)算表應(yīng)連接的順序屯烦。LEFT JOIN和STRAIGHT_JOIN強(qiáng)制的表讀順序可以幫助連接優(yōu)化器更快地工作坷随,因?yàn)闄z查的表交換更少。請(qǐng)注意這說(shuō)明如果執(zhí)行下面類(lèi)型的查詢(xún)驻龟,MySQL進(jìn)行全掃描b温眉,因?yàn)長(zhǎng)EFT JOIN強(qiáng)制它在d之前讀取:

SELECT * FROM a,b LEFT JOIN c ON (c.key=a.key) LEFT JOIN d ON (d.key=a.key) WHERE b.key=d.key;  

在這種情況下修復(fù)時(shí)用a的相反順序翁狐,b列于FROM子句中:

SELECT * FROM b,a LEFT JOIN c ON (c.key=a.key) LEFT JOIN d ON (d.key=a.key) WHERE b.key=d.key;

MySQL可以進(jìn)行下面的LEFT JOIN優(yōu)化:如果對(duì)于產(chǎn)生的NULL行类溢,WHERE條件總為假,LEFT JOIN變?yōu)槠胀?lián)接露懒。
例如闯冷,在下面的查詢(xún)中如果t2.column1為NULL,WHERE 子句將為false:

SELECT * FROM t1 LEFT JOIN t2 ON (column1) WHERE t2.column2=5;

因此懈词,可以安全地將查詢(xún)轉(zhuǎn)換為普通聯(lián)接:

SELECT * FROM t1, t2 WHERE t2.column2=5 AND t1.column1=t2.column1;  

這樣可以更快蛇耀,因?yàn)槿绻梢允共樵?xún)更佳,MySQL可以在表t1之前使用表t2坎弯。為了強(qiáng)制使用表順序纺涤,使用RIGHT_JOIN。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末抠忘,一起剝皮案震驚了整個(gè)濱河市撩炊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌崎脉,老刑警劉巖拧咳,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異囚灼,居然都是意外死亡骆膝,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)灶体,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)阅签,“玉大人,你說(shuō)我怎么就攤上這事赃春∮湓瘢” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵织中,是天一觀的道長(zhǎng)锥涕。 經(jīng)常有香客問(wèn)我,道長(zhǎng)狭吼,這世上最難降的妖魔是什么层坠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮刁笙,結(jié)果婚禮上破花,老公的妹妹穿的比我還像新娘谦趣。我一直安慰自己,他們只是感情好座每,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布前鹅。 她就那樣靜靜地躺著,像睡著了一般峭梳。 火紅的嫁衣襯著肌膚如雪舰绘。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天葱椭,我揣著相機(jī)與錄音捂寿,去河邊找鬼。 笑死孵运,一個(gè)胖子當(dāng)著我的面吹牛秦陋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播治笨,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼驳概,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了大磺?” 一聲冷哼從身側(cè)響起抡句,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤探膊,失蹤者是張志新(化名)和其女友劉穎杠愧,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體逞壁,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡流济,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了腌闯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绳瘟。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖姿骏,靈堂內(nèi)的尸體忽然破棺而出糖声,到底是詐尸還是另有隱情,我是刑警寧澤分瘦,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布蘸泻,位于F島的核電站,受9級(jí)特大地震影響嘲玫,放射性物質(zhì)發(fā)生泄漏悦施。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一去团、第九天 我趴在偏房一處隱蔽的房頂上張望抡诞。 院中可真熱鬧穷蛹,春花似錦、人聲如沸昼汗。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)顷窒。三九已至扮超,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蹋肮,已是汗流浹背出刷。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坯辩,地道東北人馁龟。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像漆魔,于是被迫代替她去往敵國(guó)和親坷檩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容