其背景是,我想计算基于订阅的业务的MRR(每月经常性收入),其中收入是在销售时记录的,以及合同的时间段,由有效结束日期表示。
然后,任务是将收入分成合同期限,并聚合多个合同,以便显示每月收入。如果能同时显示客户流失,也就是说,当合同到期而未续签时,所发生的美元损失,将是一个利好。
数据的格式如下(忽略计费周期):

╔══════════════════════════════════╦══════════════════════════╦══════════════════════════╦════════════════╦═══════╗
║            account_id            ║        start_date        ║    effective_end_date    ║ billing_period ║ price ║
╠══════════════════════════════════╬══════════════════════════╬══════════════════════════╬════════════════╬═══════╣
║ 2c92a0fd5286d62801528d6578230fa7 ║ 2015-10-01T00:00:00.000Z ║ 2017-10-15T00:00:00.000Z ║ Annual         ║ 1440  ║
║ 2c92a0fd5286d62801528d6578230fa8 ║ 2015-10-01T00:00:00.000Z ║ 2016-10-15T00:00:00.000Z ║ Annual         ║ 3500  ║
║ 2c92a0fd5286d62801528d6578230fa9 ║ 2015-10-01T00:00:00.000Z ║ 2015-12-31T00:00:00.000Z ║ Annual         ║ 700  ║
╚══════════════════════════════════╩══════════════════════════╩══════════════════════════╩════════════════╩═══════╝

预期结果如下:
2c92a0fd5286d62801528d6578230fa7在2015年10月至2017年10月的24个月内利差1440美元。
2c92a0fd5286d62801528d6578230fa8在2015年10月至2016年10月的12个月内利差3500美元。
2c92a0fd5286d62801528d6578230fa9从2015年10月到2015年12月,3个月内利差700美元。
我意识到我需要使用日期表来交叉连接,因为否则所有日期都不会被表示出来。我可以用CTE做这个。但我更困惑的是如何分配收入。任何帮助都将不胜感激!
到目前为止,我已经做到了:
SELECT account_id, date_trunc('month',effective_start_date) as start_date, effective_end_date, mrr as price,
EXTRACT(YEAR FROM age(date_trunc('month',effective_end_date)::date,date_trunc('month', effective_start_date)::date))*12 + EXTRACT(month from age(date_trunc('month',effective_end_date)::date,date_trunc('month', effective_start_date)::date)) as contract_length_months,
mrr/NULLIF(EXTRACT(YEAR FROM age(date_trunc('month',effective_end_date)::date,date_trunc('month', effective_start_date)::date))*12 + EXTRACT(month from age(date_trunc('month',effective_end_date)::date,date_trunc('month', effective_start_date)::date)),0) as divided_price
FROM "public"."zuora_rate_plan_charge" where mrr <> 0 and mrr is not null
order by date_trunc('month',effective_start_date)

结果:
╔══════════════════════════════════╦══════════════════════════╦══════════════════════════╦═══════╦════════════════════════╦═══════════════╗
║            account_id            ║        start_date        ║    effective_end_date    ║ price ║ contract_length_months ║ divided_price ║
╠══════════════════════════════════╬══════════════════════════╬══════════════════════════╬═══════╬════════════════════════╬═══════════════╣
║ 2c92a0fd5286d62801528d6578230fa7 ║ 2015-10-01T00:00:00.000Z ║ 2017-10-15T00:00:00.000Z ║  1440 ║                     24 ║            60 ║
╚══════════════════════════════════╩══════════════════════════╩══════════════════════════╩═══════╩════════════════════════╩═══════════════╝

预期结果:
╔════════╦════════════════╗
║ Month  ║      MRR       ║
╠════════╬════════════════╣
║ Oct 15 ║ 585            ║
║ Nov 15 ║ 585            ║
║ Dec 15 ║ 585            ║
║ Jan 16 ║ 351.6666666667 ║
╚════════╩════════════════╝

最佳答案

您可以使用generate_series()来获取月份,然后使用一些算法来获取数据。要获得一个客户的所有月份:

select t.*, price / count(*) over (partition by account_id) as monthy
from (select t.*,
             generate_series(start_date, end_date, interval '1 month') as yyyymm
      from t
     ) t;

然后,如果您想要每月的金额,您可以将其相加:
select yyyymm, sum(monthly)
from (select t.*,
             price / count(*) over (partition by account_id) as monthly
      from (select t.*,
                   generate_series(start_date, end_date, interval '1 month') as yyyymm
            from t
           ) t
      ) t
group by yyyymm
order by yyyymm;

10-07 15:08