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.