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);