Postgresql获取每个结果的下X行

ssm49v7z  于 2023-04-20  发布在  PostgreSQL
关注(0)|答案(4)|浏览(112)

我有以下数据:
| 销售ID|销售日期|分期付款|总值|
| --------------|--------------|--------------|--------------|
| 1|2023年1月1日|二|100.0|
| 1|2023/02/01|二|0.0|
| 1|2023年3月1日|二|0.0|
| 1|2023/04/01|三|90分|
| 1|2023年05月01日|三|0.0|
| 1|2023/06/01|三|0.0|
| 二|2023年1月1日|1|100.0|
我需要将销售_id的total_value除以接下来的X期。示例:在这种情况下,第一行应该是50.00,第二行也是50.00,如下所示:
| 销售ID|销售日期|分期付款|总值|
| --------------|--------------|--------------|--------------|
| 1|2023年1月1日|二|50.0|
| 1|2023/02/01|二|50.0|
| 1|2023年3月1日|二|0.0|
| 1|2023/04/01|三|30.0|
| 1|2023年05月01日|三|30.0|
| 1|2023/06/01|三|30.0|
| 二|2023年1月1日|1|100.0|
我的第一个想法是获取total_vale〉0的所有内容,并根据分期付款列为每一行获取接下来的X行,但我找不到一种方法来通过单个SQL查询获取它。
也许我错过了一些简单的东西,但有人可以帮助我的想法?我也试图通过函数在PLpgsql,但我没有一个很好的性能与这种方法。
我不能只更新一次所有内容,因为客户需要保留原始的数据布局。

fafcakar

fafcakar1#

每当出现新的非零分期付款时创建新组,然后使用此组进行进一步分析:
dbfiddle demo

with grps as (
  select sale_id, sale_date, installments, total_value,
         sum(case when total_value <> 0 then 1 end) over (partition by sale_id 
                                                          order by sale_date) grp
  from data)
select sale_id, sale_date, installments, total_value, grp,
       case when row_number() over (partition by sale_id, grp order by sale_date) 
              <= installments
            then max(total_value) over (partition by sale_id, grp) / installments 
            else 0
       end val
from grps
aoyhnmkz

aoyhnmkz2#

正如Atmo建议的那样,installments-row_number()max(total_value) per销售_id和installments应该正确划分它。

select  sale_id
       ,sale_date
       ,installments
       ,case when installments-row_number() over(partition by sale_id, flg order by sale_date) >= 0 then max(total_value) over(partition by sale_id, flg)/installments else 0 end as total_value     
from 
(
select  *
       ,count (case when total_value > 0 then 1 end) over(partition by sale_id order by sale_date) as flg 
from    t
) t
销售ID销售日期分期付款总值
12019 -01- 21 00:00:00五十
12019 -02- 21 00:00:00五十
12019 -03-01 00:00:000
12019 -04-01 00:00:00三十
12023-05-01 00:00:00三十
12023-06-01 00:00:00三十
2019 -01- 21 00:00:001一百

Fiddle

0yycz8jy

0yycz8jy3#

这里有一个与其他答案不同的方法(也是我在评论中最初的想法),它不需要窗口函数,它使用generate_series

作为奖励,它支持分期付款期间重叠(但如果没有任何重叠,它也可以正常工作),如果现有的分期付款不足以存储所有分期付款,它将生成行。

在这一点上,考虑到total_value = 0的记录基本上是不必要的,你可以考虑删除它们,使表更轻,更快(在FULL SCAN的情况下)访问。我想这取决于你在问题中谈论的约束。
使用下面的SELECT查询:

SELECT sale_id, sale_date, MAX(installments), SUM(total_value) AS total_value
FROM (
    SELECT sale_id, generate_series(sale_date, sale_date + make_interval(months => installments - 1), interval '1 month')::date, installments, total_value / installments
    FROM MyTable
) T(sale_id, sale_date, installments, total_value)
GROUP BY sale_id, sale_date
ORDER BY sale_id, sale_date

我被迫对installments列做了一些奇怪的事情,也许拥有额外的记录并不是你想要的。
要在这些点上“恢复”原始表的行为,请执行JOININNER JOIN恢复installments并删除额外的记录,LEFT OUTER JOIN仅恢复installments):

SELECT T.sale_id, T.sale_date, MyTable.installments, SUM(T.total_value) AS total_value
FROM (
    SELECT sale_id, generate_series(sale_date, sale_date + make_interval(months => installments - 1), interval '1 month')::date, installments, total_value / installments
    FROM MyTable
) T(sale_id, sale_date, installments, total_value)
JOIN MyTable ON MyTable.sale_id = T.sale_id AND MyTable.sale_date = T.sale_date
GROUP BY T.sale_id, T.sale_date, MyTable.installments
ORDER BY T.sale_id, T.sale_date

您可以使用它们中的任何一个来创建视图。
但是,如果你的目标是更新表,你不必关心installments列(它不会被更新),也不必关心额外的记录(UPDATE不会创建记录),所以UPDATE查询是:

UPDATE MyTable
SET total_value = T2.total_value
FROM (
    SELECT sale_id, sale_date, SUM(total_value) AS total_value
    FROM (
        SELECT sale_id, generate_series(sale_date, sale_date + make_interval(months => installments - 1), interval '1 month')::date, installments, total_value / installments
        FROM MyTable
    ) T1(sale_id, sale_date, installments, total_value)
    GROUP BY sale_id, sale_date
) T2
WHERE MyTable.sale_id = T2.sale_id AND MyTable.sale_date = T2.sale_date

只是要小心它(即启动一个可以回滚的事务),因为如果出现任何错误,这个查询就不能重复。

nfs0ujit

nfs0ujit4#

数据将使用cte根据销售_id和installments的数量进行分组,每个组将使用row_number函数获得不同的数字。此外,我们将获得每个组的最大total_value
则每组的total_value将为max_total_value/installments,其中row_number〈= installments

with cte as (
  select *, 
         row_number() over (partition by sale_id, installments order by sale_id, sale_date) as rn,
         row_number() over (order by sale_id, sale_date) - row_number() over (partition by sale_id, installments order by sale_id, sale_date) as grp,
         max(total_value) over (partition by sale_id, installments) as max_total_value
  from mytable
)
select sale_id, sale_date, installments,
       max(case when rn <= installments 
                then max_total_value/installments 
                else 0 end
          ) over (partition by grp, rn) as t_value
from cte c1

Demo here
在阅读了评论并从@Atmo得到一些评论后,我得出结论,它可能是采取的:

with cte as (
  select *, 
         sum(case when total_value <> 0 then 1 end) over (partition by sale_id order by sale_date) as grp
  from mytable
),
cte2 as (
  select *, max(total_value) over (partition by grp, sale_id) as max_total_value,
            row_number() over (partition by grp, sale_id order by sale_date) as rn
  from cte
)
select sale_id, sale_date, installments,
       max(case when rn <= installments 
                then max_total_value/installments 
                else 0 end
          ) over (partition by grp, rn, sale_id) as t_value
from cte2
order by sale_id, sale_date

第一个CTE将创建新的组每当新的非零分期付款出现。
第二个CTE将通过基于0的组获得最大值和行号。
Demo here

相关问题