使用coalesce和join计算多行中一列之间的差异

时间:2014-09-05 12:54:32

标签: mysql sql

我有下表

+-----+--------+-----------+-----------+-------+
| id  | job_id | source_id | target_id | value |
+-----+--------+-----------+-----------+-------+
| 204 |   5283 |       247 |       228 |  1201 |
| 349 |   4006 |       247 |       228 |   100 |
| 350 |   4007 |       247 |       228 |   500 |
| 351 |   4008 |       247 |       228 |  1000 |
| 352 |   4009 |         1 |       100 |   100 |
| 353 |   4010 |         1 |       100 |   500 |
| 354 |   4011 |         1 |       100 |    50 |
+-----+--------+-----------+-----------+-------+

我想在source_id和target_id分组的列之间创建差异。较旧的(较小的id)应与较新的

进行比较

我搜索了一下,发现coalesce。我写了一个小查询,它在" general"中工作,但没有被驱逐:

SELECT
    c.id, c.source_id, c.target_id, c.value, COALESCE(c1.value - c.value, -1) AS diff
FROM
    changes c LEFT JOIN changes c1 ON (c1.source_id = c.source_id AND c1.target_id = c.target_id)
GROUP BY c.source_id, c.target_id, c.job_id
ORDER BY c.id

我得到了以下结果:

+-----+-----------+-----------+-------+------+
| id  | source_id | target_id | value | diff |
+-----+-----------+-----------+-------+------+
| 204 |       247 |       228 |  1201 |    0 |
| 349 |       247 |       228 |   100 | 1101 |
| 350 |       247 |       228 |   500 |  701 |
| 351 |       247 |       228 |  1000 |  201 |
| 352 |         1 |       100 |   100 |    0 |
| 353 |         1 |       100 |   500 | -400 |
| 354 |         1 |       100 |    50 |   50 |
+-----+-----------+-----------+-------+------+

你可以看到id为349和353的差异工作,我希望这对所有行都有,如下面的预期结果:

+-----+-----------+-----------+-------+------+
| id  | source_id | target_id | value | diff |
+-----+-----------+-----------+-------+------+
| 204 |       247 |       228 |  1201 | 1201 |
| 349 |       247 |       228 |   100 | 1101 |
| 350 |       247 |       228 |   500 | -400 |
| 351 |       247 |       228 |  1000 | -500 |
| 352 |         1 |       100 |   100 |  100 |
| 353 |         1 |       100 |   500 | -400 |
| 354 |         1 |       100 |    50 |  450 |
+-----+-----------+-----------+-------+------+

如果反转差异结果将没有问题。

我错过了什么?
感谢任何提示。

3 个答案:

答案 0 :(得分:3)

我怀疑你正在寻找类似的东西 - 尽管COALESCE对我来说似乎有误导性......

SELECT a.*, COALESCE(b.value-a.value,a.value) diff
  FROM
     ( SELECT x.* , COUNT(*) rank FROM changes x JOIN  changes y ON y.id <= x.id GROUP BY x.id ) a
  LEFT
  JOIN
     ( SELECT x.* , COUNT(*) rank FROM changes x JOIN  changes y ON y.id <= x.id GROUP BY x.id ) b
    ON b.source_id = a.source_id
   AND b.rank = a.rank - 1;

答案 1 :(得分:3)

如果您使用用户定义的变量,则不需要将表连接到自身。只是按行进行比较

SELECT 
  id, 
  job_id, 
  target_id, 
  if(@a = source_id, @b - value, value) as diff, 
  @b := value as value, 
  @a := source_id as source_id 
FROM changes
CROSS JOIN (SELECT @a:=0, @b:=0)t

DEMO

答案 2 :(得分:2)

我想你想要:

   SELECT c.id,
          c.source_id,
          c.target_id,
          c.value,
          c.value - COALESCE(co.value, 0) delta
     FROM changes c
LEFT JOIN (
       SELECT ci.id, MAX(cio.id) prev_id
         FROM changes ci
         JOIN changes cio
           ON cio.source_id = ci.source_id 
          AND cio.target_id = ci.target_id
          AND cio.id < ci.id
     GROUP BY ci.id
          ) link
       ON link.id = c.id
LEFT JOIN changes co
       ON co.id = link.prev_id
 ORDER BY c.id

我稍微改变了逻辑。

在您的预期结果中,第一个差异从未知(0?)变为1201并报告为正差异,但第二个差异从1201变为100,仍然报告为正。

我已将名称更改为delta,并为您提供从上一个值移至新值所需的数字。显然,如果你想要改变这个:

COALESCE(co.value-c.value, c.value) diff

它将为您提供您提供的结果(将差异500更改为-500,我相信这是一个错字)。