我有一个表格,我希望为每一行ID找到剩余金额。但是,金额的顺序是按升序排列的。
id amount
1 3
2 2
3 1
4 5
结果应如下所示:
id remainder
1 10
2 8
3 5
4 0
有关如何实现这一目标的任何想法?我猜测over子句是要走的路,但是我不能把它拼凑起来。谢谢。
答案 0 :(得分:2)
由于你没有指定你的RDBMS,我只假设它是Postgresql; - )
select *, sum(amount) over() - sum(amount) over(order by amount) as remainder
from tbl;
输出:
| ID | AMOUNT | REMAINDER |
---------------------------
| 3 | 1 | 10 |
| 2 | 2 | 8 |
| 1 | 3 | 5 |
| 4 | 5 | 0 |
工作原理:http://www.sqlfiddle.com/#!1/c446a/5
它也适用于SQL Server 2012:http://www.sqlfiddle.com/#!6/c446a/1
考虑SQL Server 2008的解决方案......
顺便说一下,你的身份证只是一个行号吗?如果是,请执行以下操作:
select
row_number() over(order by amount) as rn
, sum(amount) over() - sum(amount) over(order by amount) as remainder
from tbl
order by rn;
输出:
| RN | REMAINDER |
------------------
| 1 | 10 |
| 2 | 8 |
| 3 | 5 |
| 4 | 0 |
但如果您确实需要完整的身份证并将最小的金额移到最上面,请执行以下操作:
with a as
(
select *, sum(amount) over() - sum(amount) over(order by amount) as remainder,
row_number() over(order by id) as id_sort,
row_number() over(order by amount) as amount_sort
from tbl
)
select a.id, sort.remainder
from a
join a sort on sort.amount_sort = a.id_sort
order by a.id_sort;
输出:
| ID | REMAINDER |
------------------
| 1 | 10 |
| 2 | 8 |
| 3 | 5 |
| 4 | 0 |
请在此处查看查询进度:http://www.sqlfiddle.com/#!6/c446a/11
答案 1 :(得分:1)
我只想提供一种更简单的方法来按降序执行此操作:
select id, sum(amount) over (order by id desc) as Remainder
from t
这适用于Oracle,SQL Server 2012和Postgres。
一般解决方案需要自我加入:
select t.id, coalesce(sum(tafter.amount), 0) as Remainder
from t left outer join
t tafter
on t.id < tafter.id
group by t.id
答案 2 :(得分:-1)
SQL Server 2008回答,我无法提供SQL小提琴,它似乎剥离了begin
关键字,导致语法错误。我在我的机器上测试过这个:
create function RunningTotalGuarded()
returns @ReturnTable table(
Id int,
Amount int not null,
RunningTotal int not null,
RN int identity(1,1) not null primary key clustered
)
as
begin
insert into @ReturnTable(id, amount, RunningTotal)
select id, amount, 0 from tbl order by amount;
declare @RunningTotal numeric(16,4) = 0;
declare @rn_check int = 0;
update @ReturnTable
set
@rn_check = @rn_check + 1
,@RunningTotal =
case when rn = @rn_check then
@RunningTotal + Amount
else
1 / 0
end
,RunningTotal = @RunningTotal;
return;
end;
要获得所需的输出:
with a as
(
select *, sum(amount) over() - RunningTotal as remainder
, row_number() over(order by id) as id_order
from RunningTotalGuarded()
)
select a.id, amount_order.remainder
from a
inner join a amount_order on amount_order.rn = a.id_order;
守卫跑步总数的基本原理:http://www.ienablemuch.com/2012/05/recursive-cte-is-evil-and-cursor-is.html
选择较小的邪恶; - )