Home > Mobile >  Data Analysis in SQLite: Monthly revenue from RECURRING sales (subscription)
Data Analysis in SQLite: Monthly revenue from RECURRING sales (subscription)

Time:10-18

I am doing a case for a subscription-based company that wants an overview of their monthly sales. This means that I need to sum up the revenue from all active subscriptions, grouped by month. I have managed to work my way through the data to create a table that looks like the one below. Each row is a contract with a start date, end date, proposition (either standard or discount), and the corresponding price per month.

ContractStartDate ContractEndDate PropositionReference PropositionPrice
2018-01-03 NULL Standard 4.5
2019-01-17 NULL Discount 2
2018-02-09 2019-01-17 Standard 4.5
... ... ... ...

I want to have the revenue for each month, starting from the minimum ContractStartDate (the first time they received any revenue), for both Standard and Discount propositions. So I would like to have something that looks like this:

Month RevenueStandard RevenueDiscount
2017-07 90 30
2017-08 85.5 80
2017-09 180 60
...
2022-10 3862 1136

For each month and each proposition (Standard or Discount), I would need to go over every contract and sum up the proposition price if the startdate was after that month and the enddate was before that month (or there is no enddate).

This is the code I tried, but I feel like I am really far off the solution:

SELECT 
MonthYear, PropositionReference, 
SUM(CASE WHEN STRFTIME("%m %Y", ContractStartDate) <= MonthYear 
AND (ContractEndDate IS NULL OR STRFTIME("%m %Y", ContractEndDate) >= MonthYear) 
AND PropositionReference = "Standard" 
THEN PropositionPrice ELSE 0 END) AS RevenueStandard,
SUM(CASE WHEN STRFTIME("%m %Y", ContractStartDate) <= MonthYear 
AND (ContractEndDate IS NULL OR STRFTIME("%m %Y", ContractEndDate) >= MonthYear) 
AND PropositionReference = "Discount" 
THEN PropositionPrice ELSE 0 END) AS RevenueDiscount
FROM (SELECT *, STRFTIME("%m %Y", ContractStartDate) AS MonthYear FROM Combined)
GROUP BY MonthYear, PropositionReference
ORDER BY MonthYear, PropositionReference

CodePudding user response:

The following could be the basis if your question has been interpreted correctly.

WITH 
    range AS (
        SELECT min(contractstartdate) AS low, max(coalesce(contractenddate,date('now'))) AS high FROM combined
        ),
    t AS (
        SELECT 
            low AS month,
            (
                SELECT 
                    coalesce(sum(propositionprice),0) 
                FROM combined
                WHERE propositionreference = 'Standard'
                AND strftime('%s',low) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
            ) 
            AS stnd,
            (
                SELECT 
                    coalesce(sum(propositionprice),0) 
                FROM combined
                WHERE propositionreference = 'Discount'
                AND strftime('%s',low) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
            )
            AS dscnt
        FROM range
        UNION ALL 
            SELECT date(month,' 1 month'),
            (
                SELECT 
                    coalesce(sum(propositionprice),0) 
                FROM combined
                WHERE propositionreference = 'Standard'
                AND strftime('%s',date(month,' 1 month')) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
            ) 
            AS stnd,
            (
                SELECT 
                    coalesce(sum(propositionprice),0) 
                FROM combined
                WHERE propositionreference = 'Discount'
                AND strftime('%s',date(month,' 1 month')) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
            )
            AS dscnt
            FROM t 
            WHERE date(month,' 1 month') < (SELECT max(coalesce(contractenddate,date('now'))) FROM combined) 
            LIMIT 500 /* just in case to stop continuous loop  */
    ) 
SELECT * FROM t;

So this is

  • first creating a CTE (Common Table Expression (temp table that exists during the execution)) that consists of a single row with two values the lowest started date and the highest end date (if any null then current date ).

  • second creating another cte, but a recursive one where the first row is the first month, the second the next month .... until the last month (or in this case for a maximum of 500 iterations (to prevent accidental endless loop)).

    • each iteration retrieves the sum of the respective type for rows from the combined table that include the date being processed.

As a demo, based upon your data then:-

DROP TABLE IF EXISTS combined;
CREATE TABLE IF NOT EXISTS combined (ContractStartDate TEXT,ContractEndDate TEXT,PropositionReference TEXT,PropositionPrice REAL);
INSERT INTO combined VALUES
    ('2018-01-03',  NULL,   'Standard', 4.5)
    ,('2019-01-17', NULL,   'Discount', 2)
    ,('2018-02-09', '2019-01-17',   'Standard', 4.5)
;
WITH 
    range AS (
        SELECT min(contractstartdate) AS low, max(coalesce(contractenddate,date('now'))) AS high FROM combined
        ),
    t AS (
        SELECT 
            low AS month,
            (
                SELECT 
                    coalesce(sum(propositionprice),0) 
                FROM combined
                WHERE propositionreference = 'Standard'
                AND strftime('%s',low) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
            ) 
            AS stnd,
            (
                SELECT 
                    coalesce(sum(propositionprice),0) 
                FROM combined
                WHERE propositionreference = 'Discount'
                AND strftime('%s',low) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
            )
            AS dscnt
        FROM range
        UNION ALL 
            SELECT date(month,' 1 month'),
            (
                SELECT 
                    coalesce(sum(propositionprice),0) 
                FROM combined
                WHERE propositionreference = 'Standard'
                AND strftime('%s',date(month,' 1 month')) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
            ) 
            AS stnd,
            (
                SELECT 
                    coalesce(sum(propositionprice),0) 
                FROM combined
                WHERE propositionreference = 'Discount'
                AND strftime('%s',date(month,' 1 month')) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
            )
            AS dscnt
            FROM t 
            WHERE date(month,' 1 month') < (SELECT max(coalesce(contractenddate,date('now'))) FROM combined) 
            LIMIT 500 /* just in case to stop continuous loop  */
    ) 
SELECT * FROM t;
DROP TABLE IF EXISTS combined;

results in :-

enter image description here enter image description here

  • Related