UNION 子句html
1. SELECT ... 2. UNION [ALL | DISTINCT] SELECT ... 3. [UNION [ALL | DISTINCT] SELECT ...]
UNION 未來自多個 SELECT 語句的結果組合到一個結果集中。例子:mysql
1. mysql> SELECT 1, 2; 2. +---+---+ 3. | 1 | 2 | 4. +---+---+ 5. | 1 | 2 | 6. +---+---+ 7. mysql> SELECT 'a', 'b'; 8. +---+---+ 9. | a | b | 10. +---+---+ 11.| a | b | 12. +---+---+ 13. mysql> SELECT 1, 2 UNION SELECT 'a', 'b'; 14. +---+---+ 15. | 1 | 2 | 16. +---+---+ 17. | 1 | 2 | 18. | a | b | 19. +---+---+
結果集列名和數據類型sql
UNION 結果集的列名取自第一個 SELECT 語句的列名。ide
在每一個 SELECT 語句的相應位置列出的選定列應具備相同的數據類型。例如,第一條語句選擇的第一列應該與其餘語句選擇的第一列具備相同的類型。若是相應的 SELECT 列的數據類型不匹配,則 UNION 結果中列的類型和長度將考慮全部 SELECT 語句檢索到的值。例如,考慮如下狀況,其中列長度不受第一個 SELECT 語句的值的長度限制:函數
1. mysql> SELECT REPEAT('a',1) UNION SELECT REPEAT('b',20); 2. +----------------------+ 3. | REPEAT('a',1) | 4. +----------------------+ 5. | a | 6. | bbbbbbbbbbbbbbbbbbbb | 7. +----------------------+
聯合中的 TABLE 語句優化
從 MySQL 8.0.19 開始,在 UNION 中只要可使用 SELECT 語句,也可使用 TABLE 語句或 VALUES 語句。假設表 t1 和 t2 被建立並填充,以下所示:code
1. CREATE TABLE t1 (x INT, y INT); 2. INSERT INTO t1 VALUES ROW(4,-2),ROW(5,9); 3. 4. CREATE TABLE t2 (a INT, b INT); 5. INSERT INTO t2 VALUES ROW(1,2),ROW(3,4);
在前面的例子中,不考慮以 VALUES 開頭的查詢輸出中的列名,如下全部 UNION 查詢都會產生相同的結果:htm
1. SELECT * FROM t1 UNION SELECT * FROM t2; 2. TABLE t1 UNION SELECT * FROM t2; 3. VALUES ROW(4,-2), ROW(5,9) UNION SELECT * FROM t2; 4. SELECT * FROM t1 UNION TABLE t2; 5. TABLE t1 UNION TABLE t2; 6. VALUES ROW(4,-2), ROW(5,9) UNION TABLE t2; 7. SELECT * FROM t1 UNION VALUES ROW(4,-2),ROW(5,9); 8. TABLE t1 UNION VALUES ROW(4,-2),ROW(5,9); 9. VALUES ROW(4,-2), ROW(5,9) UNION VALUES ROW(4,-2),ROW(5,9);
要強制列名相同,請將 VALUES 放在 SELECT 語句中的左側並使用別名,以下所示:排序
1. SELECT * FROM (VALUES ROW(4,-2), ROW(5,9)) AS t(x,y) 2. UNION TABLE t2; 3. SELECT * FROM (VALUES ROW(4,-2), ROW(5,9)) AS t(x,y) 4. UNION VALUES ROW(4,-2),ROW(5,9);
UNION DISTINCT 和 UNION ALLget
默認狀況下,將從 UNION 結果中刪除重複行。可選的 DISTINCT 關鍵字具備相同的效果,可是看起來更加明確。使用可選的 ALL 關鍵字,不會刪除重複行,結果包括全部 SELECT 語句中的全部匹配行。
能夠在同一個查詢中混用 UNION ALL 和 UNION DISTINCT。混用 UNION 類型的處理方式是,DISTINCT 聯合會覆蓋其左側的全部 ALL 聯合。能夠經過使用 UNION DISTINCT 語句生成 DISTINCT 聯合,也可使用 UNION,後面不帶 DISTINCT 或 ALL 關鍵字來達到一樣的 DISTINCT 聯合效果。
在 MySQL 8.0.19 及更高版本中,當聯合語句中使用一個或多個 TABLE 語句時,UNION ALL 和 UNION DISTINCT 的工做方式相同。
聯合語句中的 ORDER BY 和 LIMIT
要將 ORDER BY 或 LIMIT 子句應用於單個 SELECT,請用圓括號括住 SELECT 並將該子句放在括號內:
1. (SELECT a FROM t1 WHERE a=10 AND B=1 ORDER BY a LIMIT 10) 2. UNION 3. (SELECT a FROM t2 WHERE a=11 AND B=2 ORDER BY a LIMIT 10);
對單個 SELECT 語句使用 ORDER BY 並不意味着行在最終結果中出現的順序,由於 UNION 默認狀況下會生成一組無序的行。所以,此上下文中的 ORDER BY 一般與 LIMIT 一塊兒使用,以肯定要爲 SELECT 檢索的選定行的子集,即便它不必定會影響這些行在最終 UNION 結果中的順序。若是 ORDER BY 在 SELECT 中沒有 LIMIT,那麼它將被優化掉,由於它不管如何都不會有任何效果。
要使用 ORDER BY 或 LIMIT 子句對整個聯合結果進行排序或限制,請將各個 SELECT 語句括起來,並將 ORDER BY 或 LIMIT 放在最後一個語句以後:
1. (SELECT a FROM t1 WHERE a=10 AND B=1) 2. UNION 3. (SELECT a FROM t2 WHERE a=11 AND B=2) 4. ORDER BY a LIMIT 10;
從 MySQL 8.0.19 開始,能夠在聯合語句中將 ORDER BY 和 LIMIT 與 TABLE 一塊兒使用,與前面講的相同,記住 TABLE 不支持 WHERE 子句。
這種 ORDER BY 不能使用包含表名的列引用(即 tbl_name.col_name)。相反,請在第一個 SELECT 語句中提供列別名,並在 ORDER BY 中引用該別名。(或者,在 ORDER BY 中使用列位置引用列。可是,不推薦這種用法。)
另外,若是要排序的列是別名,則 ORDER BY 子句必須引用別名,而不是列名。如下第一個語句是容許的,但第二個語句失敗,會報錯 Unknown column 'a' in 'order clause':
1. (SELECT a AS b FROM t) UNION (SELECT ...) ORDER BY b; 2.(SELECT a AS b FROM t) UNION (SELECT ...) ORDER BY a;
要使 UNION 結果中的行一個接一個的由每一個 SELECT 檢索的行組成,請在每一個 SELECT 中選擇一個附加列做爲排序列,並在最後一個 SELECT 以後對該列使用 ORDER BY 進行排序:
1. (SELECT 1 AS sort_col, col1a, col1b, ... FROM t1) UNION 2.(SELECT 2, col2a, col2b, ... FROM t2) ORDER BY sort_col;
要在單個 SELECT 結果中維護排序順序,請向 ORDER BY 子句添加一個輔助列:
1. (SELECT 1 AS sort_col, col1a, col1b, ... FROM t1) UNION 2. (SELECT 2, col2a, col2b, ... FROM t2) ORDER BY sort_col, col1a;
使用附加列還能夠肯定每行來自哪一個 SELECT 語句。額外的列也能夠提供其餘標識信息,例如指出表名。
UNION 的限制
在 UNION 語句中,SELECT 語句是普通的選擇語句,但有如下限制:
● 第一個 SELECT 語句中的 HIGH_PRIORITY 沒有效果。任何後續 SELECT 中的 HIGH_PRIORITY 都會產生語法錯誤。
● 只有最後一條 SELECT 語句才能使用 INTO 子句。可是,整個 UNION 結果被寫入到 INTO 輸出目標。
從 MySQL 8.0.20 開始,這兩個包含 INTO 的 UNION 變體已被棄用,在將來的 MySQL 版本中,對它們的支持將被刪除:
● 在查詢表達式的尾部查詢塊中,在 FROM 以前使用 INTO 將產生警告。例子:
1. ... UNION SELECT * INTO OUTFILE 'file_name' FROM table_name;
● 在查詢表達式的最後的帶圓括號的部分,使用 INTO(不管其相對於 FROM 的位置如何)將生成警告。例子:
1. ... UNION (SELECT * INTO OUTFILE 'file_name' FROM table_name);
這些變體之因此被棄用,是由於它們使人困惑,就好像它們是從命名錶而不是從整個查詢表達式(UNION)收集信息同樣。
不容許在 ORDER BY 子句中使用聚合函數的 UNION 查詢,會引起 ER_AGGREGATE_ORDER_FOR_UNION 錯誤。例子:
1. SELECT 1 AS foo UNION SELECT 2 ORDER BY MAX(1);
MySQL 8.0 與 MySQL 5.7 中的 UNION 處理比較
在 MySQL 8.0 中,SELECT 和 UNION 的解析器規則被重構得更加統一(每一個這樣的上下文應用相同的 SELECT 語法)並減小重複。與 MySQL 5.7 相比,這項工做產生了幾個用戶可見的效果,可能須要重寫某些語句:
● NATURAL JOIN 容許使用一個可選的 INNER 關鍵字(NATURAL INNER JOIN),符合標準 SQL。
● 容許不帶括號的右深鏈接(right-deep join)(例如 ... JOIN ... JOIN ... ON ... ON),符合標準 SQL。
● STRAIGHT_JOIN 如今容許使用 USING 子句,相似於其餘內部鏈接。
● 解析器接受查詢表達式周圍的括號。例如,容許使用 (SELECT ... UNION SELECT ...)。
● 解析器更好地遵照規範的 SQL_CACHE 和 SQL_NO_CACHE 查詢修飾符的位置。
● 之前只容許在子查詢中使用左側聯合嵌套語句,如今容許在頂層語句中使用。例如,如今容許如下語句:
1. (SELECT 1 UNION SELECT 1) UNION SELECT 1;
● 只有在非 UNION 查詢中才容許鎖定子句(FOR UPDATE、LOCK IN SHARE MODE)。這意味着包含鎖定子句的 SELECT 語句必須使用括號。此語句再也不被視爲有效:
1. SELECT 1 FOR UPDATE UNION SELECT 1 FOR UPDATE;
相反,請這樣寫:
1. (SELECT 1 FOR UPDATE) UNION (SELECT 1 FOR UPDATE);