#### 13.2.11.9 横向派生表

[](<>)[](<>)

派生表通常不能引用（依赖于）同一表中先前表的列`从`条款。从 MySQL 8.0.14 开始，派生表可以定义为横向派生表，以指定允许此类引用。

使用在讨论的语法指定非横向派生表[第 13.2.11.8 节，“派生表”](derived-tables.html).横向派生表的语法与非横向派生表的语法相同，除了关键字`侧`在派生表规范之前指定。这`侧`关键字必须在每个表之前，才能用作横向派生表。

横向派生表受以下限制：

-   横向派生表只能出现在`从`子句，可以在用逗号分隔的表列表中，也可以在连接规范中 (`加入`,`内部联接`,`交叉连接`,`左[外]加入`， 要么`右 [外] 加入`）。

-   如果横向派生表在连接子句的右操作数中并且包含对左操作数的引用，则连接操作必须是`内部联接`,`交叉连接`或`左[外]接`.

    如果表位于左操作数中，并且包含对右操作数的引用，则联接操作必须是`内部连接`,`交叉连接`或`右[外]连接`.

-   如果横向派生表引用聚合函数，则该函数的聚合查询不能是拥有`从…起`出现横向派生表的子句。

-   根据SQL标准，MySQL总是使用表函数处理连接，例如[`JSON_表（）`](json-table-functions.html#function_json-table)好像`侧面的`已经用过了。无论MySQL发布版本如何，这都是事实，这就是为什么即使在8.0.14之前的MySQL版本中，也可以加入此函数。在MySQL 8.0.14及更高版本中`侧面的`关键字是隐式的，之前不允许使用`JSON_表（）`。这也符合SQL标准。

    下面的讨论显示了横向派生表如何使某些SQL操作成为可能，这些操作无法用非横向派生表完成，或者需要效率较低的变通方法。

    假设我们想要解决这个问题：给定一个销售团队中的人员表（其中每一行描述销售团队中的一名成员），以及一个所有销售人员的表（其中每一行描述销售：销售人员、客户、金额、日期），确定每个销售人员最大销售的规模和客户。这个问题有两种解决方法。

    解决问题的第一种方法：为每个销售人员计算最大销售规模，并找到提供最大销售规模的客户。在MySQL中，可以这样做：


```
SELECT
  salesperson.name,
  -- find maximum sale size for this salesperson
  (SELECT MAX(amount) AS amount
    FROM all_sales
    WHERE all_sales.salesperson_id = salesperson.id)
  AS amount,
  -- find customer for this maximum size
  (SELECT customer_name
    FROM all_sales
    WHERE all_sales.salesperson_id = salesperson.id
    AND all_sales.amount =
         -- find maximum size, again
         (SELECT MAX(amount) AS amount
           FROM all_sales
           WHERE all_sales.salesperson_id = salesperson.id))
  AS customer_name
FROM
  salesperson;
```

该查询效率低下，因为它会为每个销售人员计算两次最大大小（第一个子查询一次，第二个子查询一次）。

我们可以通过计算每个销售人员最多一次并将其“缓存”到派生表中来尝试获得效率增益，如下修改的查询所示：

```
SELECT
  salesperson.name,
  max_sale.amount,
  max_sale_customer.customer_name
FROM
  salesperson,
  -- calculate maximum size, cache it in transient derived table max_sale
  (SELECT MAX(amount) AS amount
    FROM all_sales
    WHERE all_sales.salesperson_id = salesperson.id)
  AS max_sale,
  -- find customer, reusing cached maximum size
  (SELECT customer_name
    FROM all_sales
    WHERE all_sales.salesperson_id = salesperson.id
    AND all_sales.amount =
        -- the cached maximum size
        max_sale.amount)
  AS max_sale_customer;
```

然而，该查询在SQL-92中是非法的，因为派生表不能依赖于同一数据库中的其他表`从…起`条款派生表必须在查询的持续时间内保持不变，不包含对其他查询列的引用`从…起`子句表。如前所述，查询会产生以下错误：

```
ERROR 1054 (42S22): Unknown column 'salesperson.id' in 'where clause'
```

在SQL:1999中，如果派生表前面有`侧面的`关键字（这意味着“此派生表依赖于其左侧的以前的表”）：

```
SELECT
  salesperson.name,
  max_sale.amount,
  max_sale_customer.customer_name
FROM
  salesperson,
  -- calculate maximum size, cache it in transient derived table max_sale
  LATERAL
  (SELECT MAX(amount) AS amount
    FROM all_sales
    WHERE all_sales.salesperson_id = salesperson.id)
  AS max_sale,
  -- find customer, reusing cached maximum size
  LATERAL
  (SELECT customer_name
    FROM all_sales
    WHERE all_sales.salesperson_id = salesperson.id
    AND all_sales.amount =
        -- the cached maximum size
        max_sale.amount)
  AS max_sale_customer;
```

横向派生表不必是常数，每次顶层查询处理前一个表中的新行时，它都会被更新。

解决问题的第二种方法：如果[`选择`](select.html)列表可能会返回多个列：

```
SELECT
  salesperson.name,
  -- find maximum size and customer at same time
  (SELECT amount, customer_name
    FROM all_sales
    WHERE all_sales.salesperson_id = salesperson.id
    ORDER BY amount DESC LIMIT 1)
FROM
  salesperson;
```

这是有效的，但却是非法的。它不起作用，因为这样的子查询只能返回一列：

```
ERROR 1241 (21000): Operand should contain 1 column(s)
```

重写查询的一种尝试是从派生表中选择多个列：

```
SELECT
  salesperson.name,
  max_sale.amount,
  max_sale.customer_name
FROM
  salesperson,
  -- find maximum size and customer at same time
  (SELECT amount, customer_name
    FROM all_sales
    WHERE all_sales.salesperson_id = salesperson.id
    ORDER BY amount DESC LIMIT 1)
  AS max_sale;
```

然而，这也不起作用。派生表依赖于`销售员`表，因此在没有`侧面的`:

```
ERROR 1054 (42S22): Unknown column 'salesperson.id' in 'where clause'
```

添加`侧面的`关键字使查询合法：

```
SELECT
  salesperson.name,
  max_sale.amount,
  max_sale.customer_name
FROM
  salesperson,
  -- find maximum size and customer at same time
  LATERAL
  (SELECT amount, customer_name
    FROM all_sales
    WHERE all_sales.salesperson_id = salesperson.id
    ORDER BY amount DESC LIMIT 1)
  AS max_sale;
```

简言之`侧面的`是刚才讨论的两种方法中所有缺点的有效解决方案。
