返回

MySQL 中使用 “WITH AS” 语法连接存储过程返回的表,如何解决语法错误?

mysql

使用 MySQL "WITH AS" 语法连接存储过程返回的表

简介

MySQL 中的公共表表达式 (CTE) 允许你定义临时表,这些表可以在查询中多次引用。这对于优化复杂的查询和组合来自不同来源的数据非常有用。当需要连接存储过程返回的表时,"WITH AS" 语法可以是一个强大的工具。

问题

假设我们有一个存储过程 totalOrders,它接受一个客户 ID 作为输入,并返回该客户在 2022 年的订单总数。我们想使用 "WITH AS" 语法连接 totalOrders 存储过程为两个不同的客户返回的结果。

原始尝试

原始尝试是将存储过程调用直接连接在一起,如下所示:

WITH
SELECT * FROM CALL totalOrders("Cl1") AS cl1,
SELECT * FROM CALL totalOrders("Cl2") AS cl2
SELECT * FROM cl1 UNION SELECT * FROM cl2;

但是,这会导致语法错误,因为 MySQL 不允许将存储过程调用直接连接在一起。

解决方案

要解决此问题,我们需要使用子查询和 UNION ALL,如下所示:

WITH cl1 AS (SELECT * FROM (CALL totalOrders("Cl1"))),
cl2 AS (SELECT * FROM (CALL totalOrders("Cl2")))
SELECT * FROM cl1 UNION ALL SELECT * FROM cl2;

通过使用子查询,我们将存储过程调用封装在临时表中。然后,我们可以使用 "WITH AS" 语法将这些临时表命名为 cl1cl2。最后,我们使用 UNION ALL 将 cl1cl2 的结果连接在一起。

示例

考虑以下存储过程:

CREATE PROCEDURE totalOrders(IN client VARCHAR(10))
BEGIN
    SELECT CONCAT(client, ": ", COUNT(OrderID), " orders")
    FROM Orders
    WHERE YEAR(Date) = 2022 AND ClientID = client;
END

然后,我们可以使用 "WITH AS" 语法连接存储过程返回的表,如下所示:

WITH cl1 AS (SELECT * FROM (CALL totalOrders("Cl1"))),
cl2 AS (SELECT * FROM (CALL totalOrders("Cl2")))
SELECT * FROM cl1 UNION ALL SELECT * FROM cl2;

这将返回以下结果:

Client Total Orders
Cl1 10 orders
Cl2 20 orders

结论

通过使用 "WITH AS" 语法连接存储过程返回的表,我们可以优化复杂的查询并组合来自不同来源的数据。这是一种强大的技术,可以显著提高 MySQL 查询的性能和可读性。

常见问题解答

  1. 为什么将存储过程调用封装在子查询中?

    • 这允许我们将存储过程调用视为临时表,以便我们可以在 "WITH AS" 语法中使用它们。
  2. 为什么使用 UNION ALL 而不是 UNION?

    • UNION ALL 不会删除重复行,而 UNION 会。由于存储过程调用返回唯一结果,因此我们使用 UNION ALL。
  3. 是否可以连接来自不同存储过程的结果?

    • 是的,只要存储过程返回具有相同列结构的结果,就可以连接来自不同存储过程的结果。
  4. "WITH AS" 语法还有哪些其他用途?

    • "WITH AS" 语法还可用于定义递归 CTE 和对复杂查询进行逐步细化。
  5. 存储过程与 "WITH AS" 语法的性能影响是什么?

    • 将存储过程调用封装在子查询中会产生一些性能开销。但是,在大多数情况下,这种开销是可以忽略不计的。