【问题标题】:CROSS/OUTER APPLY in MySQLMySQL中的交叉/外部应用
【发布时间】:2016-08-20 12:50:35
【问题描述】:

我需要在 MySQL(EC2 RDS MySQL 实例)中使用 CROSS APPLY。 看起来 MySQL 无法识别 CROSS APPLY 语法。有人可以帮帮我吗?

这是查询。

SELECT ORD.ID
    ,ORD.NAME
    ,ORD.DATE
    ,ORD_HIST.VALUE
FROM ORD
CROSS APPLY (
    SELECT TOP 1 ORD_HISTORY.VALUE
    FROM ORD_HISTORY
    WHERE ORD.ID = ORD_HISTORY.ID
        AND ORD.DATE <= ORD_HISTORY.DATE
    ORDER BY ORD_HISTORY.DATE DESC
    ) ORD_HIST

【问题讨论】:

标签: mysql join subquery cross-apply


【解决方案1】:

从 MySQL 8.0.14 开始,您可以使用 LATERAL:

现在可以在派生表前面加上 LATERAL 关键字,以指定允许在同一个 FROM 子句中引用(依赖于)前面表的列。 使用 LATERAL 指定的派生表只能出现在 FROM 子句中,要么出现在用逗号分隔的表列表中,要么出现在连接规范(JOIN、INNER JOIN、CROSS JOIN、LEFT [OUTER] JOIN 或 RIGHT [OUTER] JOIN)中。横向派生表使某些 SQL 操作成为可能,这些操作不能用非横向派生表完成,或者需要效率较低的解决方法

CROSS APPLY () <=> ,LATERAL ()
OUTER APPLY () <=> LEFT JOIN LATERAL () ON 1=1

Support for LATERAL derived tables added to MySQL 8.0.14

在这种情况下:

SELECT ORD.ID
    ,ORD.NAME
    ,ORD.DATE
    ,ORD_HIST.VALUE
FROM ORD,
LATERAL (
    SELECT ORD_HISTORY.VALUE
    FROM ORD_HISTORY
    WHERE ORD.ID = ORD_HISTORY.ID
        AND ORD.DATE <= ORD_HISTORY.DATE
    ORDER BY ORD_HISTORY.DATE DESC
    LIMIT 1
    ) ORD_HIST;

如果相关子查询未返回任何行,则将跳过来自主查询的主行。在这种情况下,应该使用LEFT JOIN LATERAL

db<>fiddle demo

【讨论】:

  • 你能解释一下LEFT JOIN LATERAL () ON 1=1是什么意思吗?该语法在 MySql 8.0.18 中不起作用
  • @YvánEcarri 当然,添加了一个示例来说明这种情况。
【解决方案2】:

基于@lujas szozda 的回答:

对于交叉申请:

SELECT ORD.ID
    ,ORD.NAME
    ,ORD.DATE
    ,ORD_HIST.VALUE
FROM ORD JOIN 
LATERAL (
    SELECT ORD_HISTORY.VALUE
    FROM ORD_HISTORY
    WHERE ORD.ID = ORD_HISTORY.ID
        AND ORD.DATE <= ORD_HISTORY.DATE
    ORDER BY ORD_HISTORY.DATE DESC
    LIMIT 1
    ) ORD_HIST ON 1=1
/* ON 1=1 is just a trick to join to whatever the LATERAL returns */

对于外部应用:

SELECT ORD.ID
    ,ORD.NAME
    ,ORD.DATE
    ,ORD_HIST.VALUE
FROM ORD LEFT JOIN 
LATERAL (
    SELECT ORD_HISTORY.VALUE
    FROM ORD_HISTORY
    WHERE ORD.ID = ORD_HISTORY.ID
        AND ORD.DATE <= ORD_HISTORY.DATE
    ORDER BY ORD_HISTORY.DATE DESC
    LIMIT 1
    ) ORD_HIST ON 1=1 
/* ON 1=1 is just a trick to join to whatever the LATERAL returns */

【讨论】:

    【解决方案3】:

    您最接近的 直接 近似值是以相关子查询作为谓词的连接。

    SELECT
       ORD.ID
      ,ORD.NAME
      ,ORD.DATE
      ,ORD_HISTORY.VALUE
    FROM
      ORD
    INNER JOIN
      ORD_HISTORY
        ON  ORD_HISTORY.<PRIMARY_KEY>
            =
            (SELECT ORD_HISTORY.<PRIMARY_KEY>
               FROM ORD_HISTORY
              WHERE ORD.ID = ORD_HISTORY.ID
                AND ORD.DATE <= ORD_HISTORY.DATE
           ORDER BY ORD_HISTORY.DATE DESC
              LIMIT 1
            )
    

    但是,在您的情况下,您只需要目标表中的一个字段。这意味着您可以直接在 SELECT 语句中使用相关子查询。

    SELECT
       ORD.ID
      ,ORD.NAME
      ,ORD.DATE
      ,(SELECT ORD_HISTORY.VALUE
          FROM ORD_HISTORY
         WHERE ORD.ID = ORD_HISTORY.ID
           AND ORD.DATE <= ORD_HISTORY.DATE
      ORDER BY ORD_HISTORY.DATE DESC
         LIMIT 1
       )   AS VALUE
    FROM
      ORD
    

    【讨论】:

    • 我尝试了第一个解决方案对于 ORD 表中的 7k 记录真的很慢。真的很怀念SQL SERVER。
    猜你喜欢
    • 1970-01-01
    • 2013-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-24
    • 1970-01-01
    相关资源
    最近更新 更多