暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL的SQL语句 - 数据操作语句(13)- 子查询(10)

林员外聊编程 2020-09-30
223
横向派生表
 
派生表通常不能引用(依赖)同一 FROM 子句中前面表的列。从 MySQL 8.0.14 开始,派生表可以定义为横向派生表,以指定允许这样的引用。
 
横向派生表的语法与非横向派生表的语法相同,只是在派生表规范之前指定了关键字 LATERAL。要用作横向派生表的每个表前面必须有 LATERAL 关键字。
 
横向派生表格受以下限制:
 
● 横向派生表只能出现在 FROM 子句中,可以出现在用逗号分隔的表列表中,也可以出现在联接规范(JOININNER JOINCROSS JOINLEFT [OUTER] JOIN RIGHT [OUTER] JOIN)中。
 
● 如果横向派生表位于联接子句的右操作数中,并且包含对左操作数的引用,则联接操作必须是 INNER JOINCROSS JOIN LEFT [OUTER] JOIN
 
如果表在左操作数中并且包含对右操作数的引用,则联接操作必须是 INNER JOIN、CROSS JOIN RIGHT [OUTER] JOIN
 
● 如果横向派生表引用聚合函数,则该函数的聚合查询不能是拥有发生横向派生表的 FROM 子句的查询。
 
● 根据 SQL 标准,表函数有一个隐式的 LATERAL,因此它的行为与 8.0.14 之前的 MySQL 8.0 版本中相同。但是,根据标准,在 JSON_TABLE() 之前不允许使用 LATERAL,即使它是隐式的。
 
下面的讨论展示了横向派生表如何使某些 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 中是非法的,因为派生表不能依赖于同一 FROM 子句中的其他表。派生表在查询期间必须是恒定的,不能包含对其他 FROM 子句表列的引用。如前所述,查询将产生以下错误:
 
ERROR 1054 (42S22): Unknown column 'salesperson.id' in 'where clause'
 
在 SQL:1999 中,如果派生表前面加上 LATERAL 关键字(这意味着“此派生表依赖于其左侧的先前表”),则查询将合法:
 
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 列表中的子查询能返回多个列,则可以使用不同的解决方案:
 
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;
 
然而,这也行不通。派生表依赖于 salesperson 表,因此如果没有 LATERAL 关键字会失败:
 
ERROR 1054 (42S22): Unknown column 'salesperson.id' in 'where clause'
 
添加 LATERAL 关键字使查询合法:

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;

简言之,LATERAL 是解决上述两种方法中所有缺点的有效方法。
 
 
 
 
 
 
官方网址:
https://dev.mysql.com/doc/refman/8.0/en/lateral-derived-tables.html
 

文章转载自林员外聊编程,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论