


with
    dau as (
        -- This part of the query can be pretty much anything.
        -- The only requirement is that it have three columns:
        --   dt, user_id, inc_amt
        -- Where dt is a date and user_id is some unique identifier for a user.
        -- Each dt-user_id pair should be unique in this table.
        -- inc_amt represents the amount of value that this user created on dt.
        -- The most common case is
        --   inc_amt = incremental revenue from the user on dt
        -- If you want to do L28 growth accounting, user inc_amt=1.
        -- The version here derives everything from the tutorial.yammer_events
        -- data set provided for free by Mode.
        -- If you edit just this part to represent your data, the rest
        -- of the query should run just fine.
        select
            date(date(order_date) + random()*90*(interval '1 day')) as dt,
            user_guid as user_id,
            sum(order_gross_rev) as inc_amt
        from washio_data
        where order_date>='2014-02-03'
        and order_gross_rev>0
        group by 1,2
    ),
    -- First, set up WAU and MAU tables for future use
    wau as (
        select
            date_trunc('week',dt) as week,
            user_id,
            sum(inc_amt) as inc_amt
        from dau
        group by 1,2
    ),
    mau as (
        select
            date_trunc('month',dt) as month,
            user_id,
            sum(inc_amt) as inc_amt
        from dau
        group by 1,2
    ),
    -- This determines the cohort date of each user. In this case we are
    -- deriving it from DAU data but you can feel free to replace it with
    -- registration date if that's more appropriate.
    first_dt as (
        select
            user_id,
            min(dt) as first_dt,
            date_trunc('week',min(dt)) as first_week,
            date_trunc('month',min(dt)) as first_month
        from dau
        group by 1
    ),
    mau_decorated as (
        select
            d.month,
            d.user_id,
            d.inc_amt,
            f.first_month
        from mau d, first_dt f
        where d.user_id=f.user_id
        and inc_amt>0
    ),
    -- This is MAU growth accounting. Note that this does not require any
    -- information about inc_amt. As discussed in the articles, these 
    -- quantities satisfy some identities:
    -- MAU(t) = retained(t) + new(t) + resurrected(t)
    -- MAU(t - 1 month) = retained(t) + churned(t)
    mau_growth_accounting as (
        select
            coalesce(tm.month, lm.month + interval '1 month') as month,
            count(distinct tm.user_id) as mau,
            count(distinct case when lm.user_id is not NULL then tm.user_id
                else NULL end) as retained,
            count(distinct case when tm.first_month=tm.month then tm.user_id
                else NULL end) as new,
            count(distinct case when tm.first_month!=tm.month
                and lm.user_id is NULL then tm.user_id else NULL end
                ) as resurrected,
            -1*count(distinct case when tm.user_id is NULL then lm.user_id
                else NULL end) as churned
        from
            mau_decorated tm
            full outer join mau_decorated lm on (
                tm.user_id=lm.user_id
                and tm.month = lm.month + interval '1 month'
            )
        group by 1
    ),
    -- This is the MRR growth accounting (or growth accounting of whatever
    -- value you put in inc_amt). These also satisfy some identities:
    -- MRR(t) = retained(t) + new(t) + resurrected(t) + expansion(t)
    -- MAU(t - 1 month) = retained(t) + churned(t) + contraction(t)
    mrr_growth_accounting as (
        select
            coalesce(tm.month, lm.month + interval '1 month') as month,
            sum(tm.inc_amt) as rev,
            sum(
                case
                    when tm.user_id is not NULL and lm.user_id is not NULL
                        and tm.inc_amt>=lm.inc_amt then lm.inc_amt
                    when tm.user_id is not NULL and lm.user_id is not NULL
                        and tm.inc_amt<lm.inc_amt then tm.inc_amt
                    else 0
                end
            ) as retained,
            sum(
                case when tm.first_month=tm.month then tm.inc_amt
                else 0 end
            ) as new,
            sum(
                case when tm.month!=tm.first_month and tm.user_id is not NULL
                    and lm.user_id is not NULL and tm.inc_amt>lm.inc_amt
                    and lm.inc_amt>0 then tm.inc_amt - lm.inc_amt
                else 0 end
            ) as expansion,
            sum(
                case when tm.user_id is not NULL
                    and (lm.user_id is NULL or lm.inc_amt=0)
                    and tm.inc_amt>0 and tm.first_month!=tm.month
                    then tm.inc_amt
                else 0 end
            ) as resurrected,
            -1*sum(
                case
                    when tm.month!=tm.first_month and tm.user_id is not NULL
                        and lm.user_id is not NULL
                        and tm.inc_amt<lm.inc_amt and tm.inc_amt>0
                        then lm.inc_amt - tm.inc_amt
                else 0 end
            ) as contraction,
            -1*sum(
                case when lm.inc_amt>0 and (tm.user_id is NULL or tm.inc_amt=0)
                then lm.inc_amt else 0 end
            ) as churned
        from
            mau_decorated tm
            full outer join mau_decorated lm on (
                tm.user_id=lm.user_id
                and tm.month = lm.month + interval '1 month'
            )
        group by 1
    ),
    -- These next tables are to compute LTV via the cohorts_cumulative table.
    -- The LTV here is being computed for weekly cohorts on weekly intervals.
    -- The queries can be modified to compute it for cohorts of any size
    -- on any time window frequency.
    wau_decorated as (
        select
            week,
            w.user_id,
            w.inc_amt,
            f.first_week
        from wau w, first_dt f
        where w.user_id=f.user_id
    ),
    cohorts as (
        select
            first_week,
            week as active_week,
            floor(extract(DAYS from (week - first_week))/7.0) as weeks_since_first,
            count(distinct user_id) as users,
            sum(inc_amt) as inc_amt
        from wau_decorated
        group by 1,2,3
        order by 1,2
    ),
    cohort_sizes as (
        select
            first_week,
            users,
            inc_amt
        from cohorts
        where weeks_since_first=0
    ),
    cohorts_cumulative as (
        -- A semi-cartesian join accomplishes the cumulative behavior.
        select
            c1.first_week,
            c1.active_week,
            c1.weeks_since_first,
            c1.users,
            cs.users as cohort_num_users,
            1.0*c1.users/cs.users as retained_pctg,
            c1.inc_amt,
            sum(c2.inc_amt) as cum_amt,
            sum(c2.inc_amt)/cs.users as cum_amt_per_user
        from
            cohorts c1,
            cohorts c2,
            cohort_sizes cs
        where
            c1.first_week=c2.first_week
            and c2.weeks_since_first<=c1.weeks_since_first
            and cs.first_week=c1.first_week
        group by 1,2,3,4,5,6,7
        order by 1,2
    )

-- For cumulative LTV data use this
select * from cohorts_cumulative

-- For MAU growth accuonting use this
select * from mau_growth_accounting

-- For MRR growth accuonting use this
select * from mrr_growth_accounting

-- For uploading to the 8-ball tool use this
select
    first_week as cohort_week,
    active_week as activity_week,
    users,
    inc_amt as revenue
from cohorts_cumulative
