在 MySQL 5.6 中使用 GROUP BY 模拟 LAG

Simulate LAG with GROUP BY in MySQL 5.6

如何在 MySQL 5.6 中模拟 MySQL 8.0 中的 LAG 函数,在那里我获得具有相同 ItemID 的先前信息。我创建此插图是为了模拟 table 和我需要的查询输出。灰色数据是原始数据 table,橙色数据是前一行中具有相同 ItemID 和最近日期的数据。

我尝试在 A.ItemID = B.ItemID AND B.Date < A.Date 上自行加入 table。我也试过按 ItemID 分组,并尝试让最大日期低于当前日期。

我也试过了。

SET @lag = -1;
SELECT *, @lag PreviousInfo, @lag:=Info CurrentInfo FROM Table1

然而,这始终只是 returns 上一行的信息,并不按 ItemID 分组。

您可以使用两个相关的子查询:

select
    t.*,
    (
        select date 
        from mytable t1 
        where t1.itemid = t.itemid and t1.date < t.date
        order by t.date desc
        limit 1
    ) previous_date,
    (
        select info 
        from mytable t1 
        where t1.itemid = t.itemid and t1.date < t.date
        order by t.date desc
        limit 1
    ) previous_info
from mytable t

然而,当您需要从以前的记录中恢复更多列时,这并不能很好地扩展。在这种情况下,我们可以使用 not exists 条件进行自连接以过滤上一条记录:

select
    t.*,
    tlag.date previous_date,
    tlag.info previous_info
from mytable t
left join mytable tlag
    on  tlag.itemid = t.itemid
    and tlag.date   < t.date
    and not exists (
        select 1
        from mytable t1
        where t1.itemid = t.itemid and t1.date < t.date and t1.date > tlag.date
    )

对于两个查询的性能,请考虑 (item_id, date) 上的以下索引。您可能希望将 info 添加到索引中,例如:(item_id, date, info),尤其是对于第一个查询,因此两个子查询都被索引 覆盖

在大型数据集上最有效的方法可能是使用变量,但您必须谨慎使用:

SELECT t1.*,
       (case when (@i <> itemid)
             then (case when (@prevd := date) = null  -- never happens
                        else null
                   end)                 
             when (@stash := @prevd) = null  -- never happens
             then null
             when (@prevd := date) = null    -- never happens
             then null
             else @x
        end) as prev_d                       
FROM (SELECT t1.*
      FROM Table1 t1
      ORDER BY itemid, date
     ) t1 CROSS JOIN
     (SELECT @i := -1, @prevd := null) params;

变量的使用非常棘手,因为 MySQL 不能保证 SELECT 中表达式的计算顺序。因此,这使用 CASE 表达式来确保正确的评估顺序。

您可能不需要 MySQL 5.6 中的子查询 -- 外部查询中的 ORDER BY 可能会处理变量(在 5.6/5.7 左右的某个时候它停止工作)。