Documentation Home

13.2.11.8 派生表

本节讨论派生表的一般特征。有关以LATERAL关键字开头的横向派生表的信息,请参阅 第 13.2.11.9 节,“横向派生表”

FROM派生表是在查询子句 范围内生成表的表达式。例如,SELECT 语句FROM子句中的子查询是派生表:

SELECT ... FROM (subquery) [AS] tbl_name ...

JSON_TABLE()函数生成一个表并提供另一种创建派生表的方法:

SELECT * FROM JSON_TABLE(arg_list) [AS] tbl_name ...

该 子句是强制性的,因为子句中的每个表都 必须有一个名称。派生表中的任何列都必须具有唯一的名称。或者, 可以后跟带括号的派生表列名称列表: [AS] tbl_nameFROMtbl_name

SELECT ... FROM (subquery) [AS] tbl_name (col_list) ...

列名的数量必须与表列的数量相同。

为了便于说明,假设您有此表:

CREATE TABLE t1 (s1 INT, s2 CHAR(5), s3 FLOAT);

以下是如何在FROM 子句中使用子查询,使用示例表:

INSERT INTO t1 VALUES (1,'1',1.0);
INSERT INTO t1 VALUES (2,'2',2.0);
SELECT sb1,sb2,sb3
  FROM (SELECT s1 AS sb1, s2 AS sb2, s3*2 AS sb3 FROM t1) AS sb
  WHERE sb1 > 1;

结果:

+------+------+------+
| sb1  | sb2  | sb3  |
+------+------+------+
|    2 | 2    |    4 |
+------+------+------+

这是另一个示例:假设您想知道分组表的一组总和的平均值。这不起作用:

SELECT AVG(SUM(column1)) FROM t1 GROUP BY column1;

但是,此查询提供了所需的信息:

SELECT AVG(sum_column1)
  FROM (SELECT SUM(column1) AS sum_column1
        FROM t1 GROUP BY column1) AS t1;

请注意,子查询 ( ) 中使用的列名sum_column1在外部查询中被识别。

派生表的列名来自其选择列表:

mysql> SELECT * FROM (SELECT 1, 2, 3, 4) AS dt;
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+

要显式提供列名,请在派生表名称后加上带括号的列名列表:

mysql> SELECT * FROM (SELECT 1, 2, 3, 4) AS dt (a, b, c, d);
+---+---+---+---+
| a | b | c | d |
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+

派生表可以返回标量、列、行或表。

派生表受以下限制:

  • 派生表不能包含对其他表的引用SELECT(为此使用 LATERAL派生表;请参阅 第 13.2.11.9 节,“横向派生表”)。

  • 在 MySQL 8.0.14 之前,派生表不能包含外部引用。这是 MySQL 8.0.14 中解除的 MySQL 限制,不是 SQL 标准的限制。例如,dt以下查询中的派生表包含对外部查询 t1.b 中表的引用:t1

    SELECT * FROM t1
    WHERE t1.d > (SELECT AVG(dt.a)
                    FROM (SELECT SUM(t2.a) AS a
                          FROM t2
                          WHERE t2.b = t1.b GROUP BY t2.c) dt
                  WHERE dt.a > 10);

    该查询在 MySQL 8.0.14 及更高版本中有效。8.0.14之前,会报错:Unknown column 't1.b' in 'where clause'

EXPLAIN优化器以不需要具体化它们 的方式确定有关派生表的信息。请参阅 第 8.2.2.4 节,“通过合并或实现优化派生表、视图引用和公用表表达式”

在某些情况下,using 可能 EXPLAIN SELECT会修改表数据。如果外部查询访问任何表并且内部查询调用更改表的一行或多行的存储函数,就会发生这种情况。假设数据库中有两个表t1和 ,以及一个修改 的存储函数,创建如下所示: t2d1f1t2

CREATE DATABASE d1;
USE d1;
CREATE TABLE t1 (c1 INT);
CREATE TABLE t2 (c1 INT);
CREATE FUNCTION f1(p1 INT) RETURNS INT
  BEGIN
    INSERT INTO t2 VALUES (p1);
    RETURN p1;
  END;

直接在 an 中引用函数 EXPLAIN SELECT对 没有影响t2,如下所示:

mysql> SELECT * FROM t2;
Empty set (0.02 sec)

mysql> EXPLAIN SELECT f1(5)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: NULL
   partitions: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
     filtered: NULL
        Extra: No tables used
1 row in set (0.01 sec)

mysql> SELECT * FROM t2;
Empty set (0.01 sec)

这是因为该SELECT 语句没有引用任何表,如输出的 tableExtra列所示。以下嵌套也是如此 SELECT

mysql> EXPLAIN SELECT NOW() AS a1, (SELECT f1(5)) AS a2\G
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
     filtered: NULL
        Extra: No tables used
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS;
+-------+------+------------------------------------------+
| Level | Code | Message                                  |
+-------+------+------------------------------------------+
| Note  | 1249 | Select 2 was reduced during optimization |
+-------+------+------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT * FROM t2;
Empty set (0.00 sec)

但是,如果外部SELECT 引用任何表,优化器也会执行子查询中的语句,t2 并修改结果:

mysql> EXPLAIN SELECT * FROM t1 AS a1, (SELECT f1(5)) AS a2\G
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: <derived2>
   partitions: NULL
         type: system
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: NULL
*************************** 2. row ***************************
           id: 1
  select_type: PRIMARY
        table: a1
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: NULL
*************************** 3. row ***************************
           id: 2
  select_type: DERIVED
        table: NULL
   partitions: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
     filtered: NULL
        Extra: No tables used
3 rows in set (0.00 sec)

mysql> SELECT * FROM t2;
+------+
| c1   |
+------+
|    5 |
+------+
1 row in set (0.00 sec)

派生表优化也可以用于许多相关(标量)子查询(MySQL 8.0.24 及更高版本)。有关更多信息和示例,请参阅 第 13.2.11.7 节,“相关子查询”