在列和行之间减去和相加

时间:2019-07-16 19:51:28

标签: sql oracle

我的数据看起来像这样

id        date             total amount    adj amount
 1        2017-01-02         100           50
 1        2017-01-02          50           0
 2        2017-01-15          100          35
 2        2017-01-15          35           0
 3        2017-01-30          120           50
 3        2017-01-30         -120         -50 
 3        2017-01-30          100           50
 3        2017-01-30          50            0 
 3        2017-01-30          60            40  

输出应该看起来像,我不知道如何在行和列之间进行减法。

 id           date              due amount
1            2017-01-02         0
2            2017-01-15         0
3            2017-01-30         40

这是我当前的代码,但它可能仅适用于1和2,但绝对不适用于3。

这部分的逻辑是为每个id查找每个条目之间的到期金额。例如,id 1有两个条目,总金额为100,然后他支付了50,所以调整金额为50,而第二个条目,总金额为50,他支付了50,te调整金额为0。因此id 1到期最后的数量是0。

id 3有5个条目,首先有条目显示ID 3的总金额是120,他支付了70,所以调整金额是50,但是第一个条目是错误的,因此所有金额都被修改。那么第三个条目显示的总额为100,ID 3支付了50,因此调整金额为50。然后,第四个条目显示的总额为50,ID 3也支付了50,因此调整金额为0。条目显示总金额为60,并且ID 3支付了20,因此调整金额为40。因此,最终ID 3的应付款为40;

select distinct a.id,
            a.date,
            case when a.date=b.date and a.total_amount = b.adj_amount then a.adj_amount
                 when  a.date=b.date and a.total_amount <> b.adj_amount then ABS(a.adj_amount + b.adj_amount) 
               else a.adj_amount
              end as due_amount

            from table a,
            table b
            where a.id=b.id;

我只是想知道是否有任何函数可以在行和列之间进行这种计算。

3 个答案:

答案 0 :(得分:0)

使用GROUP BYSUM()

SELECT the_date, SUM(due_amount)
FROM tab
GROUP BY the_date;

答案 1 :(得分:0)

如果可以订购交易,类似的事情可能会起作用。请注意,我已经重命名了某些列,以帮助阐明它们的含义。我还添加了trans_seq_num列,以指示特定日期的客户交易顺序。我认为您正在寻找客户自上次付款以来仍欠的金额。

WITH sample (id, trans_seq_num, some_date, starting_balance, ending_balance) AS
(
SELECT '1',1,'2017-01-02','100','50' FROM dual UNION ALL
SELECT '1',2,'2017-01-02','50','0' FROM dual UNION ALL
SELECT '2',1,'2017-01-15','35','0' FROM dual UNION ALL
SELECT '2',2,'2017-01-15','100','35' FROM dual UNION ALL
SELECT '3',1,'2017-01-30','120','50' FROM dual UNION ALL
SELECT '3',2,'2017-01-30','-120','-50' FROM dual UNION ALL
SELECT '3',3,'2017-01-30','100','50' FROM dual UNION ALL
SELECT '3',4,'2017-01-30','50','0' FROM dual UNION ALL
SELECT '3',5,'2017-01-30','60','40' FROM dual
)
SELECT DISTINCT id,
       some_date,
       LAST_VALUE(ending_balance) OVER (PARTITION BY id ORDER BY trans_seq_num RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) day_balance
  FROM sample
  ORDER BY 1,2,3;


ID    SOME_DATE       AMOUNT_DUE     
----- --------------- ---------------
1     2017-01-02      0              
2     2017-01-15      35             
3     2017-01-30      40             

答案 2 :(得分:0)

其他人已经说过:您应该以任何方式对行进行编号。简单的顺序即可完成工作。有了这样独特的列解决方案就很简单了,我们只为每个id找到最后一行。

但是您没有命令。这是我的尝试,到目前为止看起来还不错,可能会提供临时帮助:

with q as (
    select table_a.*, 
           row_number() over (partition by id, date_, total_amount, adj_amount 
                              order by null) rn
      from table_a),
  t as (
    select a.*, 
           row_number() over (partition by id, date_, total_amount 
                              order by null) r1,
           row_number() over (partition by id, date_, adj_amount 
                              order by null) r2  
      from q a 
      where not exists (
        select 1 from q b 
          where a.id = b.id and a.date_ = b.date_ and a.rn = b.rn
            and a.total_amount = -b.total_amount and a.adj_amount = -b.adj_amount))
select id, date_, max(adj_amount) due
  from t
  where connect_by_isleaf = 1
  connect by prior id = id and prior date_ = date_ 
      and prior adj_amount = total_amount and prior r2 = r1
  group by id, date_;

dbfiddle

首先,我消除了错误。子查询t是这样做的,它很简单不存在并添加了row_number以正确处理多种情况(例如(120,50)=>(-120,-50)和再次(120,50))。

数据已清除,因此我们可以通过先前的adj_amount = total_amount递归地找到连接的行。我们必须再次使用row_numbers来处理相同的行(60,40)=>(40,0)=>(60,40)。

然后仅提取叶子,最后获取这些叶子的最大值,如果每个id存在,则它们应包含孤立的非零值。您可以添加connect_by_path()子句以查看连接是否正常工作。

分层查询的速度比其他查询慢,因此如果表很大,请警告。如果需要,首先过滤数据。

此查询适用于您的示例以及我想象和测试的其他示例。但是,即使可行,您也应该添加排序列(如果可能的话),并且可以通过有保证的简单方法来获得正确的结果。