1. 程式人生 > 資料庫 >MySQL的SQL語句 - 資料操作語句(12)- SELECT 語句(4)

MySQL的SQL語句 - 資料操作語句(12)- SELECT 語句(4)

UNION 子句

1. SELECT ...
2. UNION [ALL | DISTINCT] SELECT ...
3. [UNION [ALL | DISTINCT] SELECT ...]

UNION 將來自多個 SELECT 語句的結果組合到一個結果集中。例子:

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. +---+---+ 

結果集列名和資料型別

UNION 結果集的列名取自第一個 SELECT 語句的列名。

在每個 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 被建立並填充,如下所示:

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 查詢都會產生相同的結果:


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 ALL

預設情況下,將從 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);

官方網址: