简体   繁体   中英

Generating monthly payments table for subscription

I have the following two tables in a database:

subscriptions : Each account number can have multiple subscriptions (A,B,C), which each have their own start/end dates and annual fees.

AccountNumber Subscription StartDate EndDate AnnualFee
123 A 1/1/2022 6/5/2022 600
123 B 1/1/2022 7/5/2022 280
123 C 4/2/2022 18/8/2022 450
123 A 7/5/2022 18/8/2022 650
123 B 8/5/2022 18/8/2022 330

calendar : Consists of all days of the year from 1 Jan to 1 August.

Date WeekofYr MonthStart MonthEnd
1/1/2022 1 1/1/2022 31/1/2022
2/1/2022 1 1/1/2022 31/1/2022
... ... ... ...
31/7/2022 31 1/7/2022 31/7/2022
1/8/2022 32 1/8/2022 31/8/2022

monthly_payments (expected output): Subscriptions are billed to each account on a monthly basis. The objective I am trying to achieve is to build an output which shows the monthly payment expected, and YTD payments, from 1 Jan to 31 July. I managed to do this in python/pandas using some manual programming logic, but I was wondering if there is a way to produce this output in sql itself? It would be very efficient if possible. I am not too concerned about which SQL RDMBS to use (can be either postgres, mysql or sqlite).

AccountNumber MonthStart MonthEnd NumDaysinMonth FeesPaid YTDPaid
123 1/1/2022 31/1/2022 31 74.74 (Subscription A and B cost 880/year total. 880/365 * 31 = 74.74 dollars that needs to be paid in Jan) 74.74
123 1/2/2022 28/2/2022 28 98.33 (There is 880/365 * 28 = 67.51 that needs to be paid for subscriptions A and B just like last month. But now there is also subscription C which started on 4/2/2022, which costs 450/365 * 25 = 30.82 for this month. So total expected payment for Feb is 67.51+30.82=98.33 173.07 (98.33+74.74)
... ... ... ... ... ...
123 1/7/2022 31/7/2022 31 ... ...

For MySQL 8.0, you can use the window function SUM() OVER(PARTITION BY *expr* ORDER BY *expr*) to calculate the running total.

See db<>fiddle

SELECT
    accountnumber, monthstart, monthend, numdaysinmonth,
    ROUND(feespaid, 2) AS feespaid, 
    ROUND(SUM(feespaid) OVER(PARTITION BY accountnumber ORDER BY monthstart), 2) AS ytdpaid
FROM (
    SELECT 
    accountnumber, monthstart, monthend,
    datediff(monthend, monthstart)+1 AS numdaysinmonth, 
    SUM(annualfee/365) AS feespaid
    FROM subscriptions s
    JOIN calendar c ON c.date BETWEEN s.startdate AND s.enddate
    -- since you mentioned 1 Jan to 31 July
    WHERE c.date BETWEEN '2022-01-01' AND '2022-07-31'     
    GROUP BY accountnumber, monthstart, monthend
    ORDER BY accountnumber, monthstart, monthend
) tmp
;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM