mysql查詢時經(jīng)常會把多個查詢結(jié)果集進行合并。主要使用UNION 和 UNION ALL。兩者區(qū)別如下:
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)建站!專注于網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、微信小程序定制開發(fā)、集團企業(yè)網(wǎng)站建設(shè)等服務項目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了黑龍江免費建站歡迎大家使用!
一般在不需對結(jié)果數(shù)據(jù)作去重和排序時,使用 UNION ALL。
本文主要介紹 SQL 標準中定義的各種連接的意義和區(qū)別,例如,交叉連接( CROSS JOIN )、內(nèi)連接( INNER JOIN )、外連接( OUTER JOIN )、自然連接( NATURAL JOIN )等,并結(jié)合例子講解這些連接在 MySQL 中的語法和表現(xiàn)。
從網(wǎng)上的資料看, JOIN 更多翻譯為連接,本文中凡是出現(xiàn)中文“連接”的地方都是指 JOIN 。
本文中用到的所有例子涉及兩張表—— customers 用戶表和 orders 訂單表,其中訂單表中的 cust_id 字段表示用戶的唯一 ID,也就是用戶表的主鍵 cust_id 。兩張表的數(shù)據(jù)如下:
注:兩張表都經(jīng)過了簡化,實際業(yè)務中這兩張表肯定還包括其他字段。
英文維基百科 JOIN 詞條 對連接的定義如下:
翻譯過來就是,“連接可以根據(jù)一張(自連接)或多張表中的共同值將這些表的列數(shù)據(jù)合并為一個新的結(jié)果集,標準 SQL 定義了五種連接:內(nèi)連接、左外連接、右外連接、全外連接和交叉連接。”
也就是說,連接是 SQL 標準中定義的一種組合多張表的方式,當然一張表自身也可以和自身組合,稱為自連接。連接后得到的結(jié)果集的每一列其實都來自用于連接的多張表,不同的連接類型只是區(qū)分了這些列具體從哪張表里來,列里填充的是什么數(shù)據(jù)。
其實英文維基百科的 JOIN 詞條已經(jīng)把各種連接的類型解釋地非常清楚了,非常值得去看一下。
我們來看一下 SQL 標準中定義的各種連接類型,理解各種連接最好的方法就是把需要連接的表想象成集合,并畫出可以反映集合的交與并的情況的圖——韋恩圖,例如下圖就畫出了 SQL 中定義的幾種主要連接。
請先仔細查看一下圖中的內(nèi)容,你可以從中歸納出幾種連接類型呢?
雖然圖中畫了 7 種集合的交并情況,但是總結(jié)起來,主要是兩種連接類型在起作用——內(nèi)連接( INNER JOIN )和外連接( OUTER JOIN ),其中外連接又分為了左外連接( LEFT OUTER JOIN )、右外連接( RIGHT OUTER JOIN )和全外連接( FULL OUTER JOIN )。
下面先簡單介紹一下 SQL 標準中各種連接的定義,然后在「MySQL 中的連接」一節(jié)再用例子來演示 MySQL 中支持的各種連接。
連接既然是用來合并多張表的,那么要定義一個連接就必須指定需要連接的表,并指定可選的連接條件。例如,一個典型的 SQL 連接語句如下:
我們用表 A 和表 B 指代需要連接的兩張表,經(jīng)過 內(nèi)連接 后得到的結(jié)果集 僅 包含所有滿足 連接條件 的數(shù)據(jù);而經(jīng)過 外連接 后得到的數(shù)據(jù)集 不僅 包含滿足 連接條件 的數(shù)據(jù),還包含其他數(shù)據(jù),具體的差別是:
在上面「SQL 標準定義的主要連接」一圖中并沒有列出交叉連接,交叉連接會對連接的兩張表做笛卡爾積,也就是連接后的數(shù)據(jù)集中的行是由第一張表中的每一行與第二張表中的每一行配對而成的,而不管它們 邏輯上 是否可以搭配在一起。假設(shè)交叉連接的兩張表分別有 m 和 n 行數(shù)據(jù),那么交叉連接后的數(shù)據(jù)集就包含 m 乘以 n 行數(shù)據(jù)。
連接根據(jù)連接的條件不同,又可以區(qū)分為等值連接和非等值連接,「SQL 標準定義的主要連接」圖中畫出的連接的連接條件都是比較兩個字段是否相等,它們都是等值連接。
自然連接是等值連接的一種特殊形式,自然連接會自動選取需要連接的兩張表中字段名相同的 所有 列做相等比較,而不需要再指定連接條件了。
注:以下內(nèi)容全部基于 MySQL 5.7 版本,所有例子只保證在 MySQL 5.7 上是可以正確執(zhí)行的。
MySQL 中支持的連接類型和關(guān)鍵字如下:
上面的表示方法摘自 MySQL 5.7 版本 官方文檔 ,其中 | 表示兩者皆可出現(xiàn), [] 表示的是可選的, {} 表示的是必選的,例如 NATURAL LEFT JOIN 和 NATURAL JOIN 都是合法的。
可以看到,除了全外連接( FULL OUTER JOIN )以外, MySQL 基本支持了 SQL 標準中定義的各種連接。在 MySQL 中全外連接可以通過 UNION 合并的方式做到,當然前提是你知道自己為什么需要這么做,具體參見: Full Out Join in MySQL 。
MySQL 語法中還支持一個并不在 SQL 標準中的 STRAIGHT_JOIN ,它在 表現(xiàn)上 和內(nèi)連接或者交叉連接并無區(qū)別,只是一種給 MySQL 優(yōu)化器的一個提示, STRAIGHT_JOIN 提示 MySQL 按照語句中表的順序加載表,只有在你明確清楚 MySQL 服務器對你的 JOIN 語句做了負優(yōu)化的時候才可能用到它。
還有一點需要說明的是,根據(jù) 官方文檔 ,在 MySQL 中, JOIN 、 CROSS JOIN 和 INNER JOIN 實現(xiàn)的功能是一致的,它們在語法上是等價的。從語義上來說, CROSS JOIN 特指無條件的連接(沒有指定 ON 條件的 JOIN 或者沒有指定 WHERE 連接條件的多表 SELECT ), INNER JOIN 特指有條件的連接(指定了 ON 條件的 JOIN 或者指定了 WHERE 連接條件的多表 SELECT )。當然,如果你非要寫 ... CROSS JOIN ... ON ... 這樣的語法,也是可以執(zhí)行的,雖然寫著交叉連接,實際上執(zhí)行的是內(nèi)連接。
下面我們就用例子來看一看 MySQL 中支持的幾種連接的例子。
注:下面的例子都沒有指定 ORDER BY 子句,返回結(jié)果的順序可能會因為數(shù)據(jù)插入順序的不同而略有不同。
MySQL 的交叉連接或內(nèi)連接有兩種寫法,一種是使用 JOIN 并用 ON 或者 USING 子句指定連接條件的寫法,一種是普通的 SELECT 多表,并且用 WHERE 子句指定連接的鍵的寫法。
下面的例子是一個交叉連接:
上面的寫法等價于:
當然,第二種寫法中如果將 CROSS JOIN 替換成 JOIN 或者 INNER JOIN 也是可以正確執(zhí)行的。上面兩條語句的執(zhí)行結(jié)果如下:
可以看到共返回了 30 行結(jié)果,是兩張表的笛卡爾積。
一個內(nèi)連接的例子如下:
上面的寫法等價于:
在連接條件比較的字段相同的情況下,還可以改用 USING 關(guān)鍵字,上面的寫法等價于:
上面三條語句的返回結(jié)果如下:
可以看到只返回了符合連接條件 customers.cust_id = orders.cust_id 的 6 行結(jié)果,結(jié)果的含義是所有有訂單的用戶和他們的訂單。
左外連接和右外連接的例子如下,其中的 OUTER 關(guān)鍵字可以省略:
其中右外連接的返回與內(nèi)連接的返回是一致的(思考一下為什么),左外連接的返回結(jié)果如下:
可以看到一共返回了 8 行數(shù)據(jù),其中最后兩行數(shù)據(jù)對應的 order_id 的值為 NULL ,結(jié)果的含義是所有用戶的訂單,不管這些用戶是否已經(jīng)有訂單存在了。
根據(jù)前面介紹的自然連接的定義,自然連接會自動用參與連接的兩張表中 字段名相同 的列做等值比較,由于例子中的 customers 和 orders 表只有一列名稱相同,我們可以用自然連接的語法寫一個與上面的內(nèi)連接的例子表現(xiàn)行為一樣的語句如下:
可以看到,使用自然連接就不能再用 ON 子句指定連接條件了,因為這完全是多余的。
當然,自然連接同樣支持左外連接和右外連接。
下面用一個 customers 表自連接的例子再來說明一下自然連接,語句如下:
因為是自連接,因此必須使用 AS 指定別名,否則 MySQL 無法區(qū)分“兩個” customers 表,運行的結(jié)果如下:
可以看到結(jié)果集和 customers 表完全一致,大家可以思考一下為什么結(jié)果是這樣的。
文章之前也提到了,MySQL 還支持一種 SQL 標準中沒有定義的“方言”, STRAIGHT_JOIN , STRAIGHT_JOIN 支持帶 ON 子句的內(nèi)連接和不帶 ON 子句的交叉連接,我們來看一個 STRAIGHT_JOIN 版本的內(nèi)連接的例子:
返回結(jié)果與前面內(nèi)連接的例子是一致的,如下:
STRAIGHT_JOIN 的表現(xiàn)和 JOIN 是完全一致的,它只是一種給 MySQL 優(yōu)化器的提示,使得 MySQL 始終按照語句中表的順序讀取表(上面的例子中,MySQL 在執(zhí)行時一定會先讀取 customers 表,再讀取 orders 表),而不會做改變讀取表的順序的優(yōu)化。關(guān)于 MySQL 優(yōu)化器的話題這里不做展開,需要說明的是除非你非常清楚你在做什么,否則不推薦直接使用 STRAIGHT_JOIN 。
你能理解上面的語句是在檢索什么數(shù)據(jù)嗎?
本文主要介紹了 SQL 標準里定義的各種連接的概念,以及 MySQL 中的實現(xiàn),并通過各種例子來介紹了這些連接的區(qū)別。這些連接不一定都能在實際開發(fā)中用到,但是做到心中有知識也還是很有必要的。
那么,現(xiàn)在再回憶一下,什么是內(nèi)連接、外連接、自連接、等值連接和自然連接?他們的區(qū)別是什么?
最后,給大家留一個思考題,為什么 MySQL 中沒有左外連接或者右外連接版本的 STRAIGHT_JOIN ?
索引合并是mysql底層為我們提供的智能算法。了解索引合并的算法,有助于我們更好的創(chuàng)建索引。
索引合并是通過多個range類型的掃描并且合并它們的結(jié)果集來檢索行的。僅合并來自單個表的索引掃描,而不是跨多個表的索引掃描。合并會產(chǎn)生底層掃描的三種形式:unions(合并)、intersections(交集)、unions-of-intersections(先取交集再合并)。
以下四個例子會產(chǎn)生索引合并:
索引合并有以下已知的局限性:
1、如果查詢語句包含一個帶有嚴重AND/OR嵌套的復雜的WHERE子句而MySQL沒有選擇最佳計劃,那么可以嘗試使用以下的標志符轉(zhuǎn)換:
(x AND y) OR z = (x OR z) AND (y OR z)
(x OR y) AND z = (x AND z) OR (y AND z)
2、索引合并不適用于全文索引。
在 EXPLAIN 語句輸出的信息中,索引合并在type列中表現(xiàn)為“index_merge”,在這種情況下,key列包含使用的索引列表。
索引合并訪問方法有幾種算法,表現(xiàn)在 EXPLAIN 語句輸出的Extra字段中:
下面將更詳細地描述這些算法。優(yōu)化器根據(jù)各種可用選項的成本估計,在不同的索引合并算法和其他訪問方法之間進行選擇。
Index Merge Intersection算法
Index Merge Intersection算法對所有使用的索引執(zhí)行同步掃描,并生成從合并的索引掃描接收到的行序列的交集。
這種算法適用于當WHERE子句被轉(zhuǎn)換成多個使用AND連接的不同索引key上的范圍條件,且條件是以下兩種之一:
一、這種形式的N部分表達式,索引正好包括N個字段(所有索引字段都被覆蓋),N=1,N如果大于1就是復合索引:
二、InnoDB表主鍵上的任何范圍條件。
例子:
Index Merge Union算法
該算法類似于Index Merge Intersection算法,適用于當WHERE子句被轉(zhuǎn)換成多個使用OR連接的不同索引key上的范圍條件,且條件是以下三種之一:
一、這種形式的N部分表達式,索引正好包括N個字段(所有索引字段都被覆蓋),N=1,N如果大于1就是復合索引:
二、InnoDB表主鍵上的任何范圍條件。
三、符合Index Merge Intersection算法的條件。
例子:
Index Merge Sort-Union算法
該算法適用于當WHERE子句被轉(zhuǎn)換成多個使用OR連接的不同索引key上的范圍條件,但是不符合 Index Merge Union算法的。Index Merge Sort-Union和Index Merge Union算法的區(qū)別在于,Index Merge Sort-Union必須首先獲取所有行的行id并在返回任何行之前對它們進行排序。
例子:
有好的建議,請在下方輸入你的評論。
歡迎訪問個人博客
步驟如下:
select?*?from?(
(SELECT?uid,je,starttime,stoptime,1?as?type?from?(SELECT?id?from?members?WHERE?referer='4')?as?m?JOIN?(SELECT?uid,je,starttime,stoptime?from?`cz`?WHERE?`status`0)?as?c?on?c.uid=m.id)
union
(SELECT?uid,je,starttime,stoptime,2?as?type?from?(SELECT?id?from?members?WHERE?referer='4')?as?m?JOIN?(SELECT?uid,je,starttime,stoptime?from?`withdrawh`?WHERE?`state`=1)?as?w?on?w.uid=m.id)?
)???order?by??starttime
MySQL是一個關(guān)系型數(shù)據(jù)庫管理系統(tǒng),由瑞典MySQL AB 公司開發(fā),目前屬于 Oracle 旗下產(chǎn)品。MySQL 是最流行的關(guān)系型數(shù)據(jù)庫管理系統(tǒng)之一,在 WEB 應用方面,MySQL是最好的 RDBMS (Relational Database Management System,關(guān)系數(shù)據(jù)庫管理系統(tǒng)) 應用軟件。
MySQL是一種關(guān)系數(shù)據(jù)庫管理系統(tǒng),關(guān)系數(shù)據(jù)庫將數(shù)據(jù)保存在不同的表中,而不是將所有數(shù)據(jù)放在一個大倉庫內(nèi),這樣就增加了速度并提高了靈活性。
MySQL所使用的 SQL 語言是用于訪問數(shù)據(jù)庫的最常用標準化語言。MySQL 軟件采用了雙授權(quán)政策,分為社區(qū)版和商業(yè)版,由于其體積小、速度快、總體擁有成本低,尤其是開放源碼這一特點,一般中小型網(wǎng)站的開發(fā)都選擇 MySQL 作為網(wǎng)站數(shù)據(jù)庫。
如果字段相同, 可以直接用 UNION ALL 合并
select a, b,c from tableA where 條件A
union all
select a,b,c from tableA where 條件B
union all
......
需要將結(jié)果合并到一起顯示出來,這個時候
就需要用到union和union all關(guān)鍵字來實現(xiàn)這樣的功能,union和union all的主要區(qū)別是union all是把結(jié)果集直接合并在一起,而
union 是將union all后的結(jié)果進行一次distinct,去除重復的記錄后的結(jié)果。