在 MySQL 中模拟滞后函数

Posted

技术标签:

【中文标题】在 MySQL 中模拟滞后函数【英文标题】:Simulate lag function in MySQL 【发布时间】:2012-07-03 10:47:01 【问题描述】:
| time                | company | quote |
+---------------------+---------+-------+
| 0000-00-00 00:00:00 | GOOGLE  |    40 |
| 2012-07-02 21:28:05 | GOOGLE  |    60 |
| 2012-07-02 21:28:51 | SAP     |    60 |
| 2012-07-02 21:29:05 | SAP     |    20 |

如何在 mysql 中对这个表做一个滞后以打印引号中的差异,例如:

GOOGLE | 20
SAP    | 40  

【问题讨论】:

每个company只有两个吗?还是可变的? 我看到你这里有两家公司,但每家公司只有两行吗?如果是这样,您可以轻松使用 MAX() - MIN() 聚合。如果每个company有2行以上,那就更复杂了。 我只需要最新的两个时间戳。可能同一家公司有很多条目,但我只需要获取最新的两个时间戳并打印报价差异 如果一家公司仅由一行表示,您想在结果中返回该公司吗?如果是这样,应该返回什么差异? 在您的示例中,为什么其中一家公司的结果不是负面的?谷歌从 40 上升到 60,而 SAP 从 60 下降到 20。sqlfiddle.com/#!2/b62e1/1/0 或者你只想要绝对运动而不考虑方向(在这种情况下采用ABS(delta))? 【参考方案1】:

这是我最喜欢的 MySQL hack。

这是您模拟滞后功能的方式:

SET @quot=-1;
select time,company,@quot lag_quote, @quot:=quote curr_quote
  from stocks order by company,time;
lag_quote 保存前一行引用的值。对于第一行,@quot 是 -1。 curr_quote 保存当前行引用的值。

注意事项:

    order by 子句在这里很重要,就像它在常规中一样 窗口功能。 您可能还想对company 使用延迟,以确保您计算的是同一company 的引号中的差异。 你也可以用同样的方式实现行计数器@cnt:=@cnt+1

与使用聚合函数、存储过程或在应用服务器中处理数据等其他一些方法相比,该方案的优点在于计算上非常精简。

编辑:

现在以您提到的格式获取结果的问题:

SET @quot=0,@latest=0,company='';
select B.* from (
select A.time,A.change,IF(@comp<>A.company,1,0) as LATEST,@comp:=A.company as company from (
select time,company,quote-@quot as change, @quot:=quote curr_quote
from stocks order by company,time) A
order by company,time desc) B where B.LATEST=1;

嵌套并不相关,因此(计算上)不像看起来(语法上)那么糟糕:)

如果您需要任何帮助,请告诉我。

【讨论】:

我收到错误消息。 MySQL 的查询面板中不允许使用 DDL 和 DML 语句;只允许 SELECT 语句。将 DDL 和 DML 放在架构面板中。 虽然错误并未表明这一点,但请尝试启用“allowMultiQueries”。这是一个连接器参数。对于 JDBC 连接器,请参阅:dev.mysql.com/doc/refman/5.1/en/…。你能从 MySQL 客户端成功运行它吗? 您也可以尝试在同一个会话中单独执行这两个语句。 @javanx 你好,我是 SQL Fiddle 的作者。您提到的错误消息实际上是我处理某些类型的 MySQL 查询的方式中的一个错误。多亏了您在此处的消息,我才意识到这一点,并制定了修复它的解决方案(请参见此处,例如:sqlfiddle.com/#!2/4f8a1/2)。谢谢! 注意 您必须使变量的数据类型与您尝试滞后的数据类型保持一致。 EG,如果你想累积一个FLOAT字段,你必须将你的变量初始化为@quot=0.0,否则将不起作用。我花了一分钟才弄清楚这一点!【参考方案2】:

从 MySQL 8.0 及以上版本无需模拟LAG。它是本机支持的,

Window Function :

从在其分区内滞后(先于)当前行 N 行的行返回 expr 的值。如果没有这样的行,则返回值为默认值。例如,如果 N 为 3,则返回值为前两行的默认值。如果缺少 N 或默认值,则默认值分别为 1 和 NULL。

SELECT
     company,
     quote,
     LAG(quote) OVER(PARTITION BY company ORDER BY time) AS prev_quote
FROM tab;

DBFiddle Demo

【讨论】:

【参考方案3】:

要达到预期的结果,首先您需要找到每家公司的最后一个时间戳和倒数第二个时间戳。使用以下查询非常简单:

SELECT c.company, c.mts, max(l.ts) AS lts
  FROM (SELECT company, max(ts) AS mts FROM cq GROUP BY company) AS c
  LEFT JOIN cq l
    ON c.company = l.company AND c.mts > l.ts
 GROUP BY c.company, c.mts;

现在您必须将此子查询与原始表连接起来才能获得所需的结果:

SELECT c.company, l.quote, coalesce(l1.quote, 0),
       (l.quote - coalesce(l1.quote, 0)) AS result
  FROM (SELECT c.company, c.mts, max(l.ts) AS lts
      FROM (SELECT company, max(ts) AS mts FROM cq GROUP BY company) AS c
      LEFT JOIN cq l
        ON c.company = l.company AND c.mts > l.ts
     GROUP BY c.company, c.mts) AS c
  LEFT JOIN cq AS l ON l.company = c.company AND l.ts = c.mts
  LEFT JOIN cq AS l1 ON l1.company = c.company AND l1.ts = c.lts;

您可以在SQL Fiddle 上观察结果。

此查询仅使用标准 SQL 功能,应该适用于任何 RDBMS。

【讨论】:

很好的答案,特别是对于准备技术面试的人,因为他们经常不鼓励使用窗口函数。

以上是关于在 MySQL 中模拟滞后函数的主要内容,如果未能解决你的问题,请参考以下文章

动画在设备上滞后,但在模拟器中不滞后

Mac上的Android模拟器滞后

Android 模拟器(来自 Android Studio)滞后

如何在 MySQL < 8.0 中模拟窗口函数

在 Microsoft SQL Server 2005 中模拟 group_concat MySQL 函数?

在 Microsoft SQL Server 2005 中模拟 group_concat MySQL 函数?