【问题标题】:How can I calculate the remaining amount per row?如何计算每行的剩余金额?
【发布时间】:2012-09-04 12:55:24
【问题描述】:

我有一个表格,我想为每一行查找总数中剩余的金额。但是,金额的顺序是升序的。

id   amount
1    3
2    2
3    1
4    5

结果应如下所示:

id   remainder
1    10
2    8
3    5
4    0

关于如何实现这一点的任何想法?我猜想 over 子句是要走的路,但我不能把它拼凑起来。谢谢。

【问题讨论】:

  • 请添加标识您使用的特定 rdbms 的标签

标签: sql


【解决方案1】:

由于您没有指定您的 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 的解决方案...


顺便说一句,您的 ID 只是一个行号吗?如果是,请执行以下操作:

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 |

但如果您确实需要完整的 ID 并将最小的数量移到顶部,请执行以下操作:

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

【讨论】:

  • 我实际上又遗漏了一个规范:我正在通过外部键对这些数量进行排序。将有另一个整数 id 来划分它们。重要的是忘记我知道,但是能够在 CTE 中使用它解决了我的问题的另一部分。谢谢。
【解决方案2】:

我只想提供一种更简单的方法来按降序执行此操作:

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

【讨论】:

  • 在 100 行上运行总计等于硬盘盘片旋转 5,050 次。对于 1,000 行,它是指数级的,它等于硬盘盘片旋转 500,500 次;-) sqlblog.com/blogs/adam_machanic/archive/2006/07/12/…
  • @MichaelBuen。 . .是的我同意。所有数据库供应商都应包括对所有窗口函数的支持。
  • 说如果我想按表上的另一个 id 排序,是使用 where 子句为我划分它还是我必须按该 id 值进行分区?
  • @user1281598 。 . .在第一个版本中,如果您只想更改 id,那就是 order by。如果您想为不同的记录组(例如国家/地区)重新开始,您将添加一个分区子句。在第二个版本中,您将在联接中添加一个“=”子句。
【解决方案3】:

SQL Server 2008 答案,我无法提供 SQL Fiddle,似乎它剥离了 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

选择较小的邪恶;-)

【讨论】:

  • 我会考虑的。谢谢你给我看这个。我确实想知道,如果我想使用光标并按表中的另一个 id 分解总计,我将如何完成此操作?
猜你喜欢
  • 2022-10-07
  • 1970-01-01
  • 1970-01-01
  • 2012-07-12
  • 2021-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-21
相关资源
最近更新 更多