Reputation: 7577
I try to calculate the Monthly Recurring Revenue in a SQL Server database for subscription orders.
Getting some details from Baremetrics about MRR
How is it calculated?
Say you have 10 customers each paying you $10 a month. Here's how you would calculate MRR.
All non-monthly plans are normalized to monthly. So if a customer is paying you $120 on an annual plan, we add $10 to MRR for the next 12 months. Neat!
So, what I have done until now, is to take care of all the raw data and include upgrades, downgrades, refunds and cancelations that could affect the MRR metric and prepare the following table:
DateCreated OrderID Price Months
2013-03-05 1 49.9500 12
2013-04-01 2 5.9500 1
2013-04-08 3 7.1100 12
2013-05-15 4 42.9500 3
2013-06-12 5 13.9850 6
2013-06-27 6 7.1100 6
2013-07-29 7 21.9250 3
For the MRR now. I expect to have two columns, first with the date of starting month and year (01-03-2013 for example) and the second with the total revenue based on MRR definition.
For example, the OrderID
1 should be divided by 12 and add the result to the current and next 11 months. OrderID
2 should be all on that month since the subscription plan is for 1 month and go on.
Any idea how I can proceed with the rest of it?
Example result:
Month MRR
01-03-2013 4.1625
01-04-2013 4.1625 + 5.95 + 0.5925
01-05-2013 4.1625 + 0.5925 + 14.31
01-06-2013 4.1625 + 0.5925 + 14.31 + 2.33 + 1.185
01-07-2013 4.1625 + 0.5925 + 14.31 + 2.33 + 1.185
01-08-2013 4.1625 + 0.5925 + 2.33 + 1.185 + 7.30
01-09-2013 4.1625 + 0.5925 + 2.33 + 1.185 + 7.30
01-10-2013 4.1625 + 0.5925 + 2.33 + 1.185 + 7.30
01-11-2013 4.1625 + 0.5925 + 2.33 + 1.185
01-12-2013 4.1625 + 0.5925
01-01-2014 4.1625 + 0.5925
01-02-2014 4.1625 + 0.5925
01-03-2014 0.5925
Prices to add per month:
49.95/12 = 4.1625
5.95/1 = 5.95
7.11/12 = 0.5925
42.95/3 = 14.31
13.985/6 = 2.33
7.11/6 = 1.185
21.925/3 = 7.30
I left the operations in the MRR column on the result, to make it clear. Obviously, the result should be the sum of them.
Create the input table:
CREATE TABLE orders
(
DateCreated datetime,
OrderID int,
Price float,
Months int
);
INSERT INTO orders
VALUES (2013-03-05, 1, 49.9500, 12),
(2013-04-01, 2, 5.9500, 1),
(2013-04-08, 3, 7.1100, 12),
(2013-05-15, 4, 42.9500, 3),
(2013-06-12, 5, 13.9850, 6),
(2013-06-27, 6, 7.1100, 6),
(2013-07-29, 7, 21.92500, 3)
Upvotes: 1
Views: 1821
Reputation: 3701
I used a recursive CTE to try and get the full months range you can ever need, but I adjusted to the 1st of the month Then I joined based on whether or not the order is applicable to that month, allowing for the fact that it remains current for n-1 further months (where n is your 'Months') - i then collected all the data in JOINER (which you could also select * from to see it) then I aggregated JOINER into a month by month summation.
I adjusted all dates to the first of the month to work with
WITH MTHS AS (SELECT dateADD(day, -DAY(MIN(datecreated)) + 1, MIN(datecreated)) MD, (SELECT MAX(dateadd(month,o2.months, o2.datecreated)) FROM ORDERS o2) AS MXD FROM Orders
UNION ALL
SELECT DATEADD(month,1,MD), MTHS.MXD FROM MTHS WHERE DATEADD(month,1,MD) < MTHS.mxd),
JOINER AS
(
select MD, ord.price / ord.months mlyprice from MTHS
JOIN ORDERS ord ON dateadd(day, -day(ord.datecreated) + 1, ord.datecreated)
BETWEEN
dateadd(month,1-ord.months,MTHS.md )
AND
MTHS.md
)
SELECT J.MD,SUM(mlyprice) FROM JOINER J GROUP BY J.MD;
I think it looks promising
CREATE TABLE orders
(
DateCreated datetime,
OrderID int,
Price float,
Months int
);
INSERT INTO orders
VALUES ('2013-03-05', 1, 49.9500, 12),
('2013-04-01', 2, 5.9500, 1),
('2013-04-08', 3, 7.1100, 12),
('2013-05-15', 4, 42.9500, 3),
('2013-06-12', 5, 13.9850, 6),
('2013-06-27', 6, 7.1100, 6),
('2013-07-29', 7, 21.92500, 3)
;WITH MTHS AS (SELECT dateADD(day, -DAY(MIN(datecreated)) + 1, MIN(datecreated)) MD, (SELECT MAX(dateadd(month,o2.months, o2.datecreated)) FROM ORDERS o2) AS MXD FROM Orders
UNION ALL
SELECT DATEADD(month,1,MD), MTHS.MXD FROM MTHS WHERE DATEADD(month,1,MD) < MTHS.mxd),
JOINER AS
(
select MD, ord.price / ord.months mlyprice from MTHS
JOIN ORDERS ord ON dateadd(day, -day(ord.datecreated) + 1, ord.datecreated)
BETWEEN
dateadd(month,1-ord.months,MTHS.md )
AND
MTHS.md
)
SELECT J.MD,SUM(mlyprice) FROM JOINER J GROUP BY J.MD
Upvotes: 1