MySQL上一行计算

时间:2016-09-06 12:03:53

标签: mysql

我在MySQL查询中保存变量时遇到了一些麻烦。我的表很简单:

+--------------+-------------+
|  SUM(price)  |  timestamp  |
+--------------+-------------+
|433           |2015-05-02   |
+--------------+-------------+
|498           |2015-06-02   |
+--------------+-------------+
|440           |2015-06-04   |
+--------------+-------------+
|434           |2015-06-07   |
+--------------+-------------+
|433           |2015-06-09   |
+--------------+-------------+

请注意,时间戳不是每日/每月,也没有索引。上表是以下查询的结果:

SELECT SUM(price) AS totalvalue, timestamp AS ts FROM basetable GROUP BY timestamp ORDER BY timestamp ASC会生成一个日期列表(时间戳),其中包含该特定时间戳中所有价格的总和。 (因此,分组依次为年表顺序)

我需要的是当前行的总数与它之前的行之间的差异,如下所示:

+--------------+-------------+--------+
|  SUM(price)  |  timestamp  |  diff  |
+--------------+-------------+--------+
|433           |2015-05-02   |0       |
+--------------+-------------+--------+
|498           |2015-06-02   |65      |
+--------------+-------------+--------+
|440           |2015-06-04   |-58     |
+--------------+-------------+--------+
|434           |2015-06-07   |-6      |
+--------------+-------------+--------+
|433           |2015-06-09   |-1      |
+--------------+-------------+--------+

所有diff-column都是current row total - previous row total。 (第一行为0)我尝试做的是将当前行的总数保存在@variable中,并在下一行中检索它,但我无法使其工作。

解决方案可能很简单,但我似乎无法得到它......

更新:我尝试过(并且失败了)这些方法:

SELECT SUM(a.price) AS totalvalue, a.timestamp, SUM(a.price)-COALESCE(SUM(b.price),0) as previous_row_diff FROM basetable a LEFT JOIN basetable b on a.timestamp=b.timestamp-1

SET @prev := 0; SELECT timestamp, SUM(price), SUM(price)-@prev AS diff, @prev := SUM(price) AS diff2 FROM basetable ORDER BY timestamp ASC;

两者都来自这个网站,因为我不知道如何让这个工作......

1 个答案:

答案 0 :(得分:1)

大多数其他数据库都支持LAG(),这使得这更容易。我们可以使用变量在MySQL中模拟这个:

SELECT totalvalue, ts,
       (CASE WHEN (@save_prev := prev) = NULL THEN NULL  -- = NULL is *never* true
             WHEN (@prev := totalvalue) = NULL THEN NULL -- = NULL is *never* true
             ELSE @save_prev
        END) as prev_value
FROM (SELECT SUM(price) AS totalvalue, timestamp AS ts
      FROM basetable 
      GROUP BY timestamp
     ) t CROSS JOIN
     (SELECT @prev := 0) params
ORDER BY timestamp ASC;

然后轻松获得差异:

SELECT totalvalue, ts,
       (totalvalue -
        (CASE WHEN (@save_prev := prev) = NULL THEN NULL  -- = NULL is *never* true
              WHEN (@prev := totalvalue) = NULL THEN NULL -- = NULL is *never* true
              ELSE @save_prev
         END)
        ) as diff
FROM (SELECT SUM(price) AS totalvalue, timestamp AS ts
      FROM basetable 
      GROUP BY timestamp
     ) t CROSS JOIN
     (SELECT @prev := 0) params
ORDER BY timestamp ASC;

注意:使用变量很棘手。 MySQL不保证SELECT中表达式的评估顺序。因此,所有相关的变量计算都需要在同一个表达式中。

以上使用CASE条件的顺序评估来确保这一点。 = NULL有意识地被用作总是返回false的东西(实际上AND falseAND 1 = 0会做同样的事情)。这可确保以正确的顺序计算变量值。