派生表是一个表达式,在查询 FROM 子句范围内生成一个表。例如,SELECT 语句 FROM 子句中的子查询是派生表:
1. SELECT ... FROM (subquery) [AS] tbl_name ...
JSON_TABLE() 函数生成一个表,并提供另一种创建派生表的方法:
1. SELECT * FROM JSON_TABLE(arg_list) [AS] tbl_name ...
[AS] tbl_name 子句是必需的,因为 FROM 子句中的每个表都必须有一个名称。派生表中的任何列都必须具有唯一的名称。或者,tbl_name 后面可以跟一个用括号括起来的派生表的一系列列名:
1. SELECT ... FROM (subquery) [AS] tbl_name (col_list) ...
列名的数目必须与表的列数相同。
为了便于说明,假设有以下表:
1. CREATE TABLE t1 (s1 INT, s2 CHAR(5), s3 FLOAT);
下面是如何在 FROM 子句中使用子查询,使用示例表:
1. INSERT INTO t1 VALUES (1,‘1‘,1.0);
2. INSERT INTO t1 VALUES (2,‘2‘,2.0);
3. SELECT sb1,sb2,sb3
4. FROM (SELECT s1 AS sb1, s2 AS sb2, s3*2 AS sb3 FROM t1) AS sb
5. WHERE sb1 > 1;
结果:
1. +------+------+------+
2. | sb1 | sb2 | sb3 |
3. +------+------+------+
4. | 2 | 2 | 4 |
5. +------+------+------+
下面是另一个例子:假设想知道分组表的一组和的平均值。下面的语句不起作用:
1. SELECT AVG(SUM(column1)) FROM t1 GROUP BY column1;
但是,此查询会得到所需的信息:
1. SELECT AVG(sum_column1)
2. FROM (SELECT SUM(column1) AS sum_column1
3. FROM t1 GROUP BY column1) AS t1;
请注意,子查询中使用的列名(sum_column1)在外部查询中可以被识别。
派生表的列名来自其选择列表:
1. mysql> SELECT * FROM (SELECT 1, 2, 3, 4) AS dt;
2. +---+---+---+---+
3. | 1 | 2 | 3 | 4 |
4. +---+---+---+---+
5. | 1 | 2 | 3 | 4 |
6. +---+---+---+---+
若要显式提供列名,请在派生表名后面加上一个带圆括号的列名列表:
1. mysql> SELECT * FROM (SELECT 1, 2, 3, 4) AS dt (a, b, c, d);
2. +---+---+---+---+
3. | a | b | c | d |
4. +---+---+---+---+
5. | 1 | 2 | 3 | 4 |
6. +---+---+---+---+
派生表可以返回标量、列、行或表。
派生表受以下限制:
● 派生表不能包含对同一 SELECT 的其他表的引用(这种情况需要使用 LATERAL 派生表)
● 在 MySQL 8.0.14 之前,派生表不能包含外部引用。这个限制在 MySQL 8.0.14 中取消了,不是 SQL 标准的限制。例如,以下查询中的派生表 dt 包含外部查询中表 t1 的引用 t1.b:
1. SELECT * FROM t1
2. WHERE t1.d > (SELECT AVG(dt.a)
3. FROM (SELECT SUM(t2.a) AS a
4. FROM t2
5. WHERE t2.b = t1.b GROUP BY t2.c) dt
6. WHERE dt.a > 10);
查询在 MySQL 8.0.14 及更高版本中有效。在 8.0.14 之前,它生成一个错误:Unknown column ‘t1.b‘ in ‘where clause‘
优化器确定有关派生表的信息时,EXPLAIN 不需要具体化它们。
在某些情况下,使用 EXPLAIN SELECT 可能会修改表数据。如果外部查询访问某些表,而内部查询调用存储函数,更改了表中的一行或多行,则会发生这种情况。假设数据库 d1 中有两个表 t1 和 t2,以及修改 t2 的存储函数 f1,创建如下:
1. CREATE DATABASE d1;
2. USE d1;
3. CREATE TABLE t1 (c1 INT);
4. CREATE TABLE t2 (c1 INT);
5. CREATE FUNCTION f1(p1 INT) RETURNS INT
6. BEGIN
7. INSERT INTO t2 VALUES (p1);
8. RETURN p1;
9. END;
在 EXPLAIN SELECT 中直接引用函数对 t2 没有影响,如下所示:
1. mysql> SELECT * FROM t2;
2. Empty set (0.02 sec)
3.
4. mysql> EXPLAIN SELECT f1(5)\G
5. *************************** 1. row ***************************
6. id: 1
7. select_type: SIMPLE
8. table: NULL
9. partitions: NULL
10. type: NULL
11. possible_keys: NULL
12. key: NULL
13. key_len: NULL
14. ref: NULL
15. rows: NULL
16. filtered: NULL
17. Extra: No tables used
18. 1 row in set (0.01 sec)
19.
20. mysql> SELECT * FROM t2;
21. Empty set (0.01 sec)
这是因为 SELECT 语句没有引用任何表,可以在输出的 table 和 Extra 列中看到。以下嵌套 SELECT 也是如此:
1. mysql> EXPLAIN SELECT NOW() AS a1, (SELECT f1(5)) AS a2\G
2. *************************** 1. row ***************************
3. id: 1
4. select_type: PRIMARY
5. table: NULL
6. type: NULL
7. possible_keys: NULL
8. key: NULL
9. key_len: NULL
10. ref: NULL
11. rows: NULL
12. filtered: NULL
13. Extra: No tables used
14. 1 row in set, 1 warning (0.00 sec)
15.
16. mysql> SHOW WARNINGS;
17. +-------+------+------------------------------------------+
18. | Level | Code | Message |
19. +-------+------+------------------------------------------+
20. | Note | 1249 | Select 2 was reduced during optimization |
21. +-------+------+------------------------------------------+
22. 1 row in set (0.00 sec)
23.
24. mysql> SELECT * FROM t2;
25. Empty set (0.00 sec)
但是,如果外部 SELECT 引用某些表,优化器也会执行子查询中的语句,结果是 t2 被修改:
1. mysql> EXPLAIN SELECT * FROM t1 AS a1, (SELECT f1(5)) AS a2\G
2. *************************** 1. row ***************************
3. id: 1
4. select_type: PRIMARY
5. table: <derived2>
6. partitions: NULL
7. type: system
8. possible_keys: NULL
9. key: NULL
10. key_len: NULL
11. ref: NULL
12. rows: 1
13. filtered: 100.00
14. Extra: NULL
15. *************************** 2. row ***************************
16. id: 1
17. select_type: PRIMARY
18. table: a1
19. partitions: NULL
20. type: ALL
21. possible_keys: NULL
22. key: NULL
23. key_len: NULL
24. ref: NULL
25. rows: 1
26. filtered: 100.00
27. Extra: NULL
28. *************************** 3. row ***************************
29. id: 2
30. select_type: DERIVED
31. table: NULL
32. partitions: NULL
33. type: NULL
34. possible_keys: NULL
35. key: NULL
36. key_len: NULL
37. ref: NULL
38. rows: NULL
39. filtered: NULL
40. Extra: No tables used
41. 3 rows in set (0.00 sec)
42.
43. mysql> SELECT * FROM t2;
44. +------+
45. | c1 |
46. +------+
47. | 5 |
48. +------+
49. 1 row in set (0.00 sec)
这也意味着像这里所示的 EXPLAIN SELECT 语句可能需要执行很长时间,因为在 t1 中,BENCHMARK() 函数对每一行执行一次:
1. EXPLAIN SELECT * FROM t1 AS a1, (SELECT BENCHMARK(1000000, MD5(NOW())));
官方网址:
https://dev.mysql.com/doc/refman/8.0/en/derived-tables.html