我在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;
两者都来自这个网站,因为我不知道如何让这个工作......
答案 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 false
或AND 1 = 0
会做同样的事情)。这可确保以正确的顺序计算变量值。