本文主要介紹 SQL 標(biāo)準(zhǔn)中定義的各種連接的意義和區(qū)別,例如,交叉連接( CROSS JOIN )、內(nèi)連接( INNER JOIN )、外連接( OUTER JOIN )、自然連接( NATURAL JOIN )等,并結(jié)合例子講解這些連接在 MySQL 中的語(yǔ)法和表現(xiàn)。
成都創(chuàng)新互聯(lián)專(zhuān)注于企業(yè)全網(wǎng)整合營(yíng)銷(xiāo)推廣、網(wǎng)站重做改版、臨潼網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5技術(shù)、商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為臨潼等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。
從網(wǎng)上的資料看, JOIN 更多翻譯為連接,本文中凡是出現(xiàn)中文“連接”的地方都是指 JOIN 。
本文中用到的所有例子涉及兩張表—— customers 用戶表和 orders 訂單表,其中訂單表中的 cust_id 字段表示用戶的唯一 ID,也就是用戶表的主鍵 cust_id 。兩張表的數(shù)據(jù)如下:
注:兩張表都經(jīng)過(guò)了簡(jiǎn)化,實(shí)際業(yè)務(wù)中這兩張表肯定還包括其他字段。
英文維基百科 JOIN 詞條 對(duì)連接的定義如下:
翻譯過(guò)來(lái)就是,“連接可以根據(jù)一張(自連接)或多張表中的共同值將這些表的列數(shù)據(jù)合并為一個(gè)新的結(jié)果集,標(biāo)準(zhǔn) SQL 定義了五種連接:內(nèi)連接、左外連接、右外連接、全外連接和交叉連接?!?/p>
也就是說(shuō),連接是 SQL 標(biāo)準(zhǔn)中定義的一種組合多張表的方式,當(dāng)然一張表自身也可以和自身組合,稱(chēng)為自連接。連接后得到的結(jié)果集的每一列其實(shí)都來(lái)自用于連接的多張表,不同的連接類(lèi)型只是區(qū)分了這些列具體從哪張表里來(lái),列里填充的是什么數(shù)據(jù)。
其實(shí)英文維基百科的 JOIN 詞條已經(jīng)把各種連接的類(lèi)型解釋地非常清楚了,非常值得去看一下。
我們來(lái)看一下 SQL 標(biāo)準(zhǔn)中定義的各種連接類(lèi)型,理解各種連接最好的方法就是把需要連接的表想象成集合,并畫(huà)出可以反映集合的交與并的情況的圖——韋恩圖,例如下圖就畫(huà)出了 SQL 中定義的幾種主要連接。
請(qǐng)先仔細(xì)查看一下圖中的內(nèi)容,你可以從中歸納出幾種連接類(lèi)型呢?
雖然圖中畫(huà)了 7 種集合的交并情況,但是總結(jié)起來(lái),主要是兩種連接類(lèi)型在起作用——內(nèi)連接( INNER JOIN )和外連接( OUTER JOIN ),其中外連接又分為了左外連接( LEFT OUTER JOIN )、右外連接( RIGHT OUTER JOIN )和全外連接( FULL OUTER JOIN )。
下面先簡(jiǎn)單介紹一下 SQL 標(biāo)準(zhǔn)中各種連接的定義,然后在「MySQL 中的連接」一節(jié)再用例子來(lái)演示 MySQL 中支持的各種連接。
連接既然是用來(lái)合并多張表的,那么要定義一個(gè)連接就必須指定需要連接的表,并指定可選的連接條件。例如,一個(gè)典型的 SQL 連接語(yǔ)句如下:
我們用表 A 和表 B 指代需要連接的兩張表,經(jīng)過(guò) 內(nèi)連接 后得到的結(jié)果集 僅 包含所有滿足 連接條件 的數(shù)據(jù);而經(jīng)過(guò) 外連接 后得到的數(shù)據(jù)集 不僅 包含滿足 連接條件 的數(shù)據(jù),還包含其他數(shù)據(jù),具體的差別是:
在上面「SQL 標(biāo)準(zhǔn)定義的主要連接」一圖中并沒(méi)有列出交叉連接,交叉連接會(huì)對(duì)連接的兩張表做笛卡爾積,也就是連接后的數(shù)據(jù)集中的行是由第一張表中的每一行與第二張表中的每一行配對(duì)而成的,而不管它們 邏輯上 是否可以搭配在一起。假設(shè)交叉連接的兩張表分別有 m 和 n 行數(shù)據(jù),那么交叉連接后的數(shù)據(jù)集就包含 m 乘以 n 行數(shù)據(jù)。
連接根據(jù)連接的條件不同,又可以區(qū)分為等值連接和非等值連接,「SQL 標(biāo)準(zhǔn)定義的主要連接」圖中畫(huà)出的連接的連接條件都是比較兩個(gè)字段是否相等,它們都是等值連接。
自然連接是等值連接的一種特殊形式,自然連接會(huì)自動(dòng)選取需要連接的兩張表中字段名相同的 所有 列做相等比較,而不需要再指定連接條件了。
注:以下內(nèi)容全部基于 MySQL 5.7 版本,所有例子只保證在 MySQL 5.7 上是可以正確執(zhí)行的。
MySQL 中支持的連接類(lèi)型和關(guān)鍵字如下:
上面的表示方法摘自 MySQL 5.7 版本 官方文檔 ,其中 | 表示兩者皆可出現(xiàn), [] 表示的是可選的, {} 表示的是必選的,例如 NATURAL LEFT JOIN 和 NATURAL JOIN 都是合法的。
可以看到,除了全外連接( FULL OUTER JOIN )以外, MySQL 基本支持了 SQL 標(biāo)準(zhǔn)中定義的各種連接。在 MySQL 中全外連接可以通過(guò) UNION 合并的方式做到,當(dāng)然前提是你知道自己為什么需要這么做,具體參見(jiàn): Full Out Join in MySQL 。
MySQL 語(yǔ)法中還支持一個(gè)并不在 SQL 標(biāo)準(zhǔn)中的 STRAIGHT_JOIN ,它在 表現(xiàn)上 和內(nèi)連接或者交叉連接并無(wú)區(qū)別,只是一種給 MySQL 優(yōu)化器的一個(gè)提示, STRAIGHT_JOIN 提示 MySQL 按照語(yǔ)句中表的順序加載表,只有在你明確清楚 MySQL 服務(wù)器對(duì)你的 JOIN 語(yǔ)句做了負(fù)優(yōu)化的時(shí)候才可能用到它。
還有一點(diǎn)需要說(shuō)明的是,根據(jù) 官方文檔 ,在 MySQL 中, JOIN 、 CROSS JOIN 和 INNER JOIN 實(shí)現(xiàn)的功能是一致的,它們?cè)谡Z(yǔ)法上是等價(jià)的。從語(yǔ)義上來(lái)說(shuō), CROSS JOIN 特指無(wú)條件的連接(沒(méi)有指定 ON 條件的 JOIN 或者沒(méi)有指定 WHERE 連接條件的多表 SELECT ), INNER JOIN 特指有條件的連接(指定了 ON 條件的 JOIN 或者指定了 WHERE 連接條件的多表 SELECT )。當(dāng)然,如果你非要寫(xiě) ... CROSS JOIN ... ON ... 這樣的語(yǔ)法,也是可以執(zhí)行的,雖然寫(xiě)著交叉連接,實(shí)際上執(zhí)行的是內(nèi)連接。
下面我們就用例子來(lái)看一看 MySQL 中支持的幾種連接的例子。
注:下面的例子都沒(méi)有指定 ORDER BY 子句,返回結(jié)果的順序可能會(huì)因?yàn)閿?shù)據(jù)插入順序的不同而略有不同。
MySQL 的交叉連接或內(nèi)連接有兩種寫(xiě)法,一種是使用 JOIN 并用 ON 或者 USING 子句指定連接條件的寫(xiě)法,一種是普通的 SELECT 多表,并且用 WHERE 子句指定連接的鍵的寫(xiě)法。
下面的例子是一個(gè)交叉連接:
上面的寫(xiě)法等價(jià)于:
當(dāng)然,第二種寫(xiě)法中如果將 CROSS JOIN 替換成 JOIN 或者 INNER JOIN 也是可以正確執(zhí)行的。上面兩條語(yǔ)句的執(zhí)行結(jié)果如下:
可以看到共返回了 30 行結(jié)果,是兩張表的笛卡爾積。
一個(gè)內(nèi)連接的例子如下:
上面的寫(xiě)法等價(jià)于:
在連接條件比較的字段相同的情況下,還可以改用 USING 關(guān)鍵字,上面的寫(xiě)法等價(jià)于:
上面三條語(yǔ)句的返回結(jié)果如下:
可以看到只返回了符合連接條件 customers.cust_id = orders.cust_id 的 6 行結(jié)果,結(jié)果的含義是所有有訂單的用戶和他們的訂單。
左外連接和右外連接的例子如下,其中的 OUTER 關(guān)鍵字可以省略:
其中右外連接的返回與內(nèi)連接的返回是一致的(思考一下為什么),左外連接的返回結(jié)果如下:
可以看到一共返回了 8 行數(shù)據(jù),其中最后兩行數(shù)據(jù)對(duì)應(yīng)的 order_id 的值為 NULL ,結(jié)果的含義是所有用戶的訂單,不管這些用戶是否已經(jīng)有訂單存在了。
根據(jù)前面介紹的自然連接的定義,自然連接會(huì)自動(dòng)用參與連接的兩張表中 字段名相同 的列做等值比較,由于例子中的 customers 和 orders 表只有一列名稱(chēng)相同,我們可以用自然連接的語(yǔ)法寫(xiě)一個(gè)與上面的內(nèi)連接的例子表現(xiàn)行為一樣的語(yǔ)句如下:
可以看到,使用自然連接就不能再用 ON 子句指定連接條件了,因?yàn)檫@完全是多余的。
當(dāng)然,自然連接同樣支持左外連接和右外連接。
下面用一個(gè) customers 表自連接的例子再來(lái)說(shuō)明一下自然連接,語(yǔ)句如下:
因?yàn)槭亲赃B接,因此必須使用 AS 指定別名,否則 MySQL 無(wú)法區(qū)分“兩個(gè)” customers 表,運(yùn)行的結(jié)果如下:
可以看到結(jié)果集和 customers 表完全一致,大家可以思考一下為什么結(jié)果是這樣的。
文章之前也提到了,MySQL 還支持一種 SQL 標(biāo)準(zhǔn)中沒(méi)有定義的“方言”, STRAIGHT_JOIN , STRAIGHT_JOIN 支持帶 ON 子句的內(nèi)連接和不帶 ON 子句的交叉連接,我們來(lái)看一個(gè) STRAIGHT_JOIN 版本的內(nèi)連接的例子:
返回結(jié)果與前面內(nèi)連接的例子是一致的,如下:
STRAIGHT_JOIN 的表現(xiàn)和 JOIN 是完全一致的,它只是一種給 MySQL 優(yōu)化器的提示,使得 MySQL 始終按照語(yǔ)句中表的順序讀取表(上面的例子中,MySQL 在執(zhí)行時(shí)一定會(huì)先讀取 customers 表,再讀取 orders 表),而不會(huì)做改變讀取表的順序的優(yōu)化。關(guān)于 MySQL 優(yōu)化器的話題這里不做展開(kāi),需要說(shuō)明的是除非你非常清楚你在做什么,否則不推薦直接使用 STRAIGHT_JOIN 。
你能理解上面的語(yǔ)句是在檢索什么數(shù)據(jù)嗎?
本文主要介紹了 SQL 標(biāo)準(zhǔn)里定義的各種連接的概念,以及 MySQL 中的實(shí)現(xiàn),并通過(guò)各種例子來(lái)介紹了這些連接的區(qū)別。這些連接不一定都能在實(shí)際開(kāi)發(fā)中用到,但是做到心中有知識(shí)也還是很有必要的。
那么,現(xiàn)在再回憶一下,什么是內(nèi)連接、外連接、自連接、等值連接和自然連接?他們的區(qū)別是什么?
最后,給大家留一個(gè)思考題,為什么 MySQL 中沒(méi)有左外連接或者右外連接版本的 STRAIGHT_JOIN ?
TABLE A ;TABLE B
內(nèi)連接:A INNER JOIN B ,在A中也有,在B中也有的數(shù)據(jù)才能查詢出來(lái)
左連接:A LEFT JOIN B,連接查詢的數(shù)據(jù),在A中必須有,在B中可以有可以沒(méi)有
右連接與左連接相反
左連接:返回包括左表中的所有記錄和右表中聯(lián)結(jié)字段相等的記錄;
右連接:返回包括右表中的所有記錄和左表中聯(lián)結(jié)字段相等的記錄;
全連接:返回兩個(gè)連接表的所有記錄;mysql沒(méi)有全連接
內(nèi)連接:返回兩個(gè)連接表連接字段相同的記錄;
union:對(duì)兩個(gè)結(jié)果集進(jìn)行并集,并去重,當(dāng)交換兩個(gè)select語(yǔ)句順序后,最終結(jié)果與不交換時(shí)無(wú)區(qū)別;
union all:對(duì)兩個(gè)結(jié)果集進(jìn)行并集,不去重,當(dāng)交換兩個(gè)select語(yǔ)句順序后,最終結(jié)果與不交換時(shí)有區(qū)別;
舉個(gè)例子 有一個(gè)學(xué)生表 班級(jí) 姓名 一個(gè)成績(jī)表 姓名 成績(jī) 我們需要返回X班的所有學(xué)生的成績(jī),但是班上有人缺考,也就是成績(jī)表里沒(méi)有姓名,我們先用on得到的就是有考試成績(jī)的名字,通過(guò)外連接,我們就可以得到全班人的名字以及成績(jī)。
學(xué)生student表:
課程course表:
交叉連接:
結(jié)果為兩張表的笛卡爾積:
內(nèi)連接(inner join ,inner 可以省略)
顯示內(nèi)連接和隱示內(nèi)連接獲得的查詢結(jié)果是一樣的,都是A表和B表的交集(例:A.id = B.id),但是只能查到有關(guān)系的信息,如果A表的一條數(shù)據(jù)的與B表關(guān)聯(lián)的字段沒(méi)有對(duì)應(yīng)的信息(如:A.id = null),即:這條數(shù)據(jù)在B表中沒(méi)有對(duì)應(yīng)的信息,則無(wú)法獲得。
外連接(outer join,outer可以省略)
左外連接獲得的查詢結(jié)果是左邊的表A的全部信息和A,B兩張表的交集,左邊A表的全部包含A表中在B表中沒(méi)有對(duì)應(yīng)關(guān)系的信息。
右外連接獲得的查詢結(jié)果是右邊的表B的全部信息和A,B兩張表的交集,右邊B表的全部包含B表中在A表中沒(méi)有對(duì)應(yīng)關(guān)系的信息。
內(nèi)連接關(guān)鍵字:inner join;左連接:left join;右連接:right join。 內(nèi)連接是把匹配的關(guān)聯(lián)數(shù)據(jù)顯示出來(lái);左連接是左邊的表全部顯示出來(lái),右邊的表顯示出符合條件的數(shù)據(jù);右連接正好相反。