每个月每个唯一 ID 的累计总和
Cumulative Sum for each unique ID in every month
我正在尝试计算所有客户的累计利润,我从 here 中找到了一个很好的参考。然而,我的 table 由超过 1 个客户组成,他们应该有自己的累积利润。下面是我已有的table'client'
| Year | Month | id | profit | cumulative |
| 2017 | 1 | 123 | 1000 | |
| 2017 | 2 | 123 | -200 | |
| 2017 | 3 | 123 | 500 | |
| 2017 | 1 | 456 | 500 | |
| 2017 | 2 | 456 | 100 | |
| 2017 | 3 | 456 | 200 | |
如果我使用这样的 sql 代码:
SET @csum := 0;
UPDATE client
SET cumulative = (@csum := @csum + profit);
我得到的结果是这样的:
| Year | Month | id | profit | cumulative |
| 2017 | 1 | 123 | 1000 | 1000 |
| 2017 | 2 | 123 | -200 | 800 |
| 2017 | 3 | 123 | 500 | 1300 |
| 2017 | 1 | 456 | 500 | 1800 |
| 2017 | 2 | 456 | 100 | 1900 |
| 2017 | 3 | 456 | 200 | 2100 |
我期望得到的是这样的:
| Year | Month | id | profit | cumulative |
| 2017 | 1 | 123 | 1000 | 1000 |
| 2017 | 2 | 123 | -200 | 800 |
| 2017 | 3 | 123 | 500 | 1300 |
| 2017 | 1 | 456 | 500 | 500 |
| 2017 | 2 | 456 | 100 | 600 |
| 2017 | 3 | 456 | 200 | 800 |
我也尝试按年、月和 ID 对其进行分组,但它不起作用。基本上,我想要每个月每个唯一客户的累计金额。你知道如何解决这个问题吗?提前致谢。
我会避免局部变量,因为结果有时可能与预期不同,而且 DBMS 可以更好地优化基于集合的方法。改为使用子查询或自连接:
SELECT c1.*,
(SELECT SUM(c2.profit)
FROM client c2
WHERE (c2.year < c1.year or
(c2.year = c1.year and c2.month <= c1.month)) and
c2.id = c1.id
) AS cumulative_sum
FROM TABLE client c1
因此在update
中可以这样
UPDATE client
JOIN
(
SELECT c1.id, c1.year, c1.month,
(SELECT SUM(c2.profit)
FROM client c2
WHERE (c2.year < c1.year or
(c2.year = c1.year and c2.month <= c1.month)) and
c2.id = c1.id
) AS cumulative_sum
FROM client c1
) t ON client.id = t.id and
client.year = t.year and
client.month = t.month
SET cumulative = t.cumulative_sum
sqlfiddle demo(感谢@JohnWoo 提供数据)
局部变量只能在查询中使用 ORDER BY 才能正常工作。
SET @csum := 0, @id:=NULL;
UPDATE client
SET cumulative = (@csum := if(id=@id,@csum,0) + profit), id=(@id:=id)
ORDER BY id, year, month;
或更简短:... SET cumulative = (@csum := if(id=@id, @csum, 0*(@id:=id) ) + profit)
。此比较存储 ID 与当前 ID,return 如果 ID 相同则存储 SUM,如果 ID 不同则 return 0(并存储新 ID)。
你可以使用变量来做到这一点,但你需要非常小心。使用变量时,您希望所有操作都在一条语句中——因为 MySQL 不保证语句求值的顺序:
SET @csum := 0;
SET @id := -1;
UPDATE client c
SET cumulative = (CASE WHEN @id = id
THEN @csum := @csum + profit
WHEN @id := id
THEN @csum := profit
ELSE @csum := profit
END)
ORDER BY id, year, month;
我正在尝试计算所有客户的累计利润,我从 here 中找到了一个很好的参考。然而,我的 table 由超过 1 个客户组成,他们应该有自己的累积利润。下面是我已有的table'client'
| Year | Month | id | profit | cumulative |
| 2017 | 1 | 123 | 1000 | |
| 2017 | 2 | 123 | -200 | |
| 2017 | 3 | 123 | 500 | |
| 2017 | 1 | 456 | 500 | |
| 2017 | 2 | 456 | 100 | |
| 2017 | 3 | 456 | 200 | |
如果我使用这样的 sql 代码:
SET @csum := 0;
UPDATE client
SET cumulative = (@csum := @csum + profit);
我得到的结果是这样的:
| Year | Month | id | profit | cumulative |
| 2017 | 1 | 123 | 1000 | 1000 |
| 2017 | 2 | 123 | -200 | 800 |
| 2017 | 3 | 123 | 500 | 1300 |
| 2017 | 1 | 456 | 500 | 1800 |
| 2017 | 2 | 456 | 100 | 1900 |
| 2017 | 3 | 456 | 200 | 2100 |
我期望得到的是这样的:
| Year | Month | id | profit | cumulative |
| 2017 | 1 | 123 | 1000 | 1000 |
| 2017 | 2 | 123 | -200 | 800 |
| 2017 | 3 | 123 | 500 | 1300 |
| 2017 | 1 | 456 | 500 | 500 |
| 2017 | 2 | 456 | 100 | 600 |
| 2017 | 3 | 456 | 200 | 800 |
我也尝试按年、月和 ID 对其进行分组,但它不起作用。基本上,我想要每个月每个唯一客户的累计金额。你知道如何解决这个问题吗?提前致谢。
我会避免局部变量,因为结果有时可能与预期不同,而且 DBMS 可以更好地优化基于集合的方法。改为使用子查询或自连接:
SELECT c1.*,
(SELECT SUM(c2.profit)
FROM client c2
WHERE (c2.year < c1.year or
(c2.year = c1.year and c2.month <= c1.month)) and
c2.id = c1.id
) AS cumulative_sum
FROM TABLE client c1
因此在update
中可以这样
UPDATE client
JOIN
(
SELECT c1.id, c1.year, c1.month,
(SELECT SUM(c2.profit)
FROM client c2
WHERE (c2.year < c1.year or
(c2.year = c1.year and c2.month <= c1.month)) and
c2.id = c1.id
) AS cumulative_sum
FROM client c1
) t ON client.id = t.id and
client.year = t.year and
client.month = t.month
SET cumulative = t.cumulative_sum
sqlfiddle demo(感谢@JohnWoo 提供数据)
局部变量只能在查询中使用 ORDER BY 才能正常工作。
SET @csum := 0, @id:=NULL;
UPDATE client
SET cumulative = (@csum := if(id=@id,@csum,0) + profit), id=(@id:=id)
ORDER BY id, year, month;
或更简短:... SET cumulative = (@csum := if(id=@id, @csum, 0*(@id:=id) ) + profit)
。此比较存储 ID 与当前 ID,return 如果 ID 相同则存储 SUM,如果 ID 不同则 return 0(并存储新 ID)。
你可以使用变量来做到这一点,但你需要非常小心。使用变量时,您希望所有操作都在一条语句中——因为 MySQL 不保证语句求值的顺序:
SET @csum := 0;
SET @id := -1;
UPDATE client c
SET cumulative = (CASE WHEN @id = id
THEN @csum := @csum + profit
WHEN @id := id
THEN @csum := profit
ELSE @csum := profit
END)
ORDER BY id, year, month;