【问题标题】:PostgreSQL users and balances - use previous balance for user in time series if value is missingPostgreSQL 用户和余额 - 如果缺少值,则在时间序列中为用户使用以前的余额
【发布时间】:2022-10-13 03:42:15
【问题描述】:

给定下表:

用户

name
alice
bob

余额

id user_name date balance
1 alice 2022-01-01 100
2 alice 2022-01-03 200
3 alice 2022-01-04 300
4 bob 2022-01-01 400
5 bob 2022-01-02 500
6 bob 2022-01-05 600

我想获得所有用户从第一天到最后一天的完整列表,用该用户的最后可用余额替换 NULL 余额。

这是我到目前为止所拥有的:

select u.name, s.day, b.balance
from users u
cross join (select generate_series(min(day)::date, max(day)::date, interval '1 day')::date as day from balances) s
left join balances b on b.user_name = u.name and s.day = b.day
order by u.name, s.day 
;

SQL Fiddle Here

我已经尝试过LAG() 和此处找到的其他一些示例,但它们似乎都没有为用户获得正确的最后平衡。

【问题讨论】:

    标签: sql postgresql left-join


    【解决方案1】:

    我们要使用 running_total:sum() over()

    select      u.name
               ,s.day
               ,sum(b.balance) over(partition by u.name order by s.day) as balance
    from        users u
    cross join (select generate_series(min(day)::date, max(day)::date, interval '1 day')::date as day from balances) s
    left join   balances b on b.user_name = u.name and s.day = b.day
    order by    u.name, s.day 
    
    name day balance
    alice 2022-01-01 100
    alice 2022-01-02 100
    alice 2022-01-03 300
    alice 2022-01-04 600
    alice 2022-01-05 600
    bob 2022-01-01 400
    bob 2022-01-02 900
    bob 2022-01-03 900
    bob 2022-01-04 900
    bob 2022-01-05 1500

    Fiddle

    【讨论】:

    • 此解决方案将所有先前余额的总和相加。我需要它来添加最后一个余额值。例如。 bob 的 2022-01-05 余额应该是 600
    【解决方案2】:

    基于How do I efficiently select the previous non-null value?,我最终通过以下查询获得了成功的结果:

    select
      name, 
      day, 
      first_value(balance) over (partition by x.name, value_partition order by day) as balance
    from (
      select 
        u.name as name, 
        s.day as day, 
        b.balance as balance,
        sum(case when b.balance is null then 0 else 1 end) over (partition by u.name order by s.day) as value_partition
      from users u
      cross join (select generate_series(min(day)::date, max(day)::date, interval '1 day')::date as day from balances) s
      left join balances b on b.user_name = u.name and s.day = b.day
    ) x
    order by x.name, x.day 
    

    DB Fiddle

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-16
      • 2020-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-15
      相关资源
      最近更新 更多