SQL Ecommerce beauty

Customers Earned Churned Last 7

Customers Earned Churned Last 7 model for ecommerce beauty.

source-preserved

Blocks

customers_earned_churned_last_7.sql · 130 lines
inferred
-- models/customers_earned_churned_last_7.sql
{{ config(materialized='table') }}

select
# acq.iov_vs_aov, ltr.average_ltr,
count(*) as customers_earned
, max(ltr.average_ltr) as 12_month_average_ltr
, sum(acq.iov_vs_aov * ltr.average_ltr) as 12_month_ltr_earned
, b.*
from
(
  # Initial OV as % of AOV over last 6 months
  select soc.user_id, sum(soc.total / 100.0) / count(*) /
    (
    select sum(soc.total / 100.0) / count(*)
    from order_consolidated soc
    where soc.completed_at between date_add(curdate(), INTERVAL -6 month) and date_add(curdate(), INTERVAL -1 second)
    ) as iov_vs_aov

  from order_consolidated soc
  where soc.completed_at between (date_add(curdate(), INTERVAL -8 day)) and (date_add(curdate(), INTERVAL -1 second))
  group by 1
) acq
inner join
(
  select sum(12_month_ltr) / count(*) as average_ltr
  from (
    select
    fp.user_id
    , sum(soc.total / 100.0) as 12_month_ltr
  # , soc.completed_at
  # , fp.date_first_purchase
  # , datediff(soc.completed_at, fp.date_first_purchase)
    from

    (
      select soc.user_id, min(completed_at) as date_first_purchase
      from order_consolidated soc
      group by 1
      having date_first_purchase BETWEEN (date_add(curdate(), INTERVAL -730 day)) AND (date_add(date_add(curdate(), INTERVAL -365 day), INTERVAL -1 second))
    ) fp
     left join order_consolidated as soc
        on soc.user_id = fp.user_id
          and datediff(soc.completed_at, fp.date_first_purchase) <= 365
    group by 1

  ) a
) ltr
inner join (
select
ch.churned_customers as customers_churned
, (revenue_per_0_12 - revenue_per_13plus) as 12_month_lost_ltr_per_churn
, ch.churned_customers * (revenue_per_0_12 - revenue_per_13plus) as 12_month_ltr_churned
from (
  # metrics on active vs lapsed
  select
  (
    # 12-month revenue per 0-12 customer
    select sum(soc.total / 100.0) / count(*) as revenue_per_0_12
    from
    (
      select soc.user_id
      from order_consolidated soc
      where soc.completed_at BETWEEN (date_add(curdate(), INTERVAL -730 day)) AND (date_add(date_add(curdate(), INTERVAL -365 day), INTERVAL -1 second))
      and soc.user_id is not null
      group by 1
    ) a
    inner join order_consolidated soc
    on soc.user_id = a.user_id
    and soc.completed_at between date_add(curdate(), INTERVAL -364 day) AND date_add(curdate(), INTERVAL -1 second)
  ) as revenue_per_0_12,
  (
    # 12-month revenue per 13+ customer
    select sum(soc.total / 100.0) / count(*) as revenue_per_13plus
  # , sum(soc.total/100.0) as revenue
  # , count(*) as users
    from (
      # acquired ast least 1 year prior as of 1 year ago
      select soc.user_id
      from order_consolidated soc
      where completed_at <= date_add(curdate(), interval -730 day)
    ) u
    # their orders in the last year
    left join order_consolidated soc
    on soc.user_id = u.user_id
    and soc.completed_at between date_add(curdate(), INTERVAL -364 day) AND date_add(curdate(), INTERVAL -1 second)
    left join (
      # and didn't buy in the next 12 months
      select soc.user_id
      from order_consolidated soc
      where soc.completed_at between date_add(curdate(), INTERVAL -730 day) AND date_add(curdate(), interval -365 day)
      group by 1
    ) b
    on b.user_id = u.`user_id`
    where b.user_id is null
  ) as revenue_per_13plus
) a
inner join (
  # churned from active to lapsed
  select count(1) as churned_customers
  from (
    select soc.user_id
    , min(soc.completed_at) as first_order_date
    , max(soc.completed_at) as last_order_date
    from order_consolidated soc
    where soc.completed_at between  (date_add(curdate(), INTERVAL -371 DAY)) and (date_add(curdate(), INTERVAL -1 second))
    group by 1
    having first_order_date between date_add(curdate(), INTERVAL -371 DAY) and date_add(curdate(), INTERVAL -364 day)
    and last_order_date between date_add(curdate(), INTERVAL -371 DAY) and date_add(curdate(), INTERVAL -364 day)
  ) a
) ch
) b
;

# models/schema.yml
version: 2

models:
  - name: customers_earned_churned_last_7
    columns:
      - name: customers_earned
      - name: 12_month_average_ltr
      - name: 12_month_ltr_earned
      - name: customers_churned
      - name: 12_month_lost_ltr_per_churn
      - name: 12_month_ltr_churned
      - name: 12_month_average_ltr_earned
      - name: 12_month_net_ltr_earn_churn
      - name: annualized_earn_churn
      - name: estimated_ecomm_run_rate

Assumptions

  • materialized='table' inferred from a LookML persist hint (persist_for / datagroup / sql_trigger_value).
  • schema.yml columns come from view dimensions; a primary_key dimension gets unique + not_null tests.
customers_earned_churned_last_7.sql · 110 lines
source-preserved validation: not checked
select
# acq.iov_vs_aov, ltr.average_ltr,
count(*) as customers_earned
, max(ltr.average_ltr) as 12_month_average_ltr
, sum(acq.iov_vs_aov * ltr.average_ltr) as 12_month_ltr_earned
, b.*
from
(
  # Initial OV as % of AOV over last 6 months
  select soc.user_id, sum(soc.total / 100.0) / count(*) /
    (
    select sum(soc.total / 100.0) / count(*)
    from order_consolidated soc
    where soc.completed_at between date_add(curdate(), INTERVAL -6 month) and date_add(curdate(), INTERVAL -1 second)
    ) as iov_vs_aov

  from order_consolidated soc
  where soc.completed_at between (date_add(curdate(), INTERVAL -8 day)) and (date_add(curdate(), INTERVAL -1 second))
  group by 1
) acq
inner join
(
  select sum(12_month_ltr) / count(*) as average_ltr
  from (
    select
    fp.user_id
    , sum(soc.total / 100.0) as 12_month_ltr
  # , soc.completed_at
  # , fp.date_first_purchase
  # , datediff(soc.completed_at, fp.date_first_purchase)
    from

    (
      select soc.user_id, min(completed_at) as date_first_purchase
      from order_consolidated soc
      group by 1
      having date_first_purchase BETWEEN (date_add(curdate(), INTERVAL -730 day)) AND (date_add(date_add(curdate(), INTERVAL -365 day), INTERVAL -1 second))
    ) fp
     left join order_consolidated as soc
        on soc.user_id = fp.user_id
          and datediff(soc.completed_at, fp.date_first_purchase) <= 365
    group by 1

  ) a
) ltr
inner join (
select
ch.churned_customers as customers_churned
, (revenue_per_0_12 - revenue_per_13plus) as 12_month_lost_ltr_per_churn
, ch.churned_customers * (revenue_per_0_12 - revenue_per_13plus) as 12_month_ltr_churned
from (
  # metrics on active vs lapsed
  select
  (
    # 12-month revenue per 0-12 customer
    select sum(soc.total / 100.0) / count(*) as revenue_per_0_12
    from
    (
      select soc.user_id
      from order_consolidated soc
      where soc.completed_at BETWEEN (date_add(curdate(), INTERVAL -730 day)) AND (date_add(date_add(curdate(), INTERVAL -365 day), INTERVAL -1 second))
      and soc.user_id is not null
      group by 1
    ) a
    inner join order_consolidated soc
    on soc.user_id = a.user_id
    and soc.completed_at between date_add(curdate(), INTERVAL -364 day) AND date_add(curdate(), INTERVAL -1 second)
  ) as revenue_per_0_12,
  (
    # 12-month revenue per 13+ customer
    select sum(soc.total / 100.0) / count(*) as revenue_per_13plus
  # , sum(soc.total/100.0) as revenue
  # , count(*) as users
    from (
      # acquired ast least 1 year prior as of 1 year ago
      select soc.user_id
      from order_consolidated soc
      where completed_at <= date_add(curdate(), interval -730 day)
    ) u
    # their orders in the last year
    left join order_consolidated soc
    on soc.user_id = u.user_id
    and soc.completed_at between date_add(curdate(), INTERVAL -364 day) AND date_add(curdate(), INTERVAL -1 second)
    left join (
      # and didn't buy in the next 12 months
      select soc.user_id
      from order_consolidated soc
      where soc.completed_at between date_add(curdate(), INTERVAL -730 day) AND date_add(curdate(), interval -365 day)
      group by 1
    ) b
    on b.user_id = u.`user_id`
    where b.user_id is null
  ) as revenue_per_13plus
) a
inner join (
  # churned from active to lapsed
  select count(1) as churned_customers
  from (
    select soc.user_id
    , min(soc.completed_at) as first_order_date
    , max(soc.completed_at) as last_order_date
    from order_consolidated soc
    where soc.completed_at between  (date_add(curdate(), INTERVAL -371 DAY)) and (date_add(curdate(), INTERVAL -1 second))
    group by 1
    having first_order_date between date_add(curdate(), INTERVAL -371 DAY) and date_add(curdate(), INTERVAL -364 day)
    and last_order_date between date_add(curdate(), INTERVAL -371 DAY) and date_add(curdate(), INTERVAL -364 day)
  ) a
) ch
) b
;
customers_earned_churned_last_7.view.lkml · 169 lines
inferred
view: customers_earned_churned_last_7 {
  derived_table: {
    sql: select
# acq.iov_vs_aov, ltr.average_ltr,
count(*) as customers_earned
, max(ltr.average_ltr) as 12_month_average_ltr
, sum(acq.iov_vs_aov * ltr.average_ltr) as 12_month_ltr_earned
, b.*
from
(
  # Initial OV as % of AOV over last 6 months
  select soc.user_id, sum(soc.total / 100.0) / count(*) /
    (
    select sum(soc.total / 100.0) / count(*)
    from order_consolidated soc
    where soc.completed_at between date_add(curdate(), INTERVAL -6 month) and date_add(curdate(), INTERVAL -1 second)
    ) as iov_vs_aov

  from order_consolidated soc
  where soc.completed_at between (date_add(curdate(), INTERVAL -8 day)) and (date_add(curdate(), INTERVAL -1 second))
  group by 1
) acq
inner join
(
  select sum(12_month_ltr) / count(*) as average_ltr
  from (
    select
    fp.user_id
    , sum(soc.total / 100.0) as 12_month_ltr
  # , soc.completed_at
  # , fp.date_first_purchase
  # , datediff(soc.completed_at, fp.date_first_purchase)
    from

    (
      select soc.user_id, min(completed_at) as date_first_purchase
      from order_consolidated soc
      group by 1
      having date_first_purchase BETWEEN (date_add(curdate(), INTERVAL -730 day)) AND (date_add(date_add(curdate(), INTERVAL -365 day), INTERVAL -1 second))
    ) fp
     left join order_consolidated as soc
        on soc.user_id = fp.user_id
          and datediff(soc.completed_at, fp.date_first_purchase) <= 365
    group by 1

  ) a
) ltr
inner join (
select
ch.churned_customers as customers_churned
, (revenue_per_0_12 - revenue_per_13plus) as 12_month_lost_ltr_per_churn
, ch.churned_customers * (revenue_per_0_12 - revenue_per_13plus) as 12_month_ltr_churned
from (
  # metrics on active vs lapsed
  select
  (
    # 12-month revenue per 0-12 customer
    select sum(soc.total / 100.0) / count(*) as revenue_per_0_12
    from
    (
      select soc.user_id
      from order_consolidated soc
      where soc.completed_at BETWEEN (date_add(curdate(), INTERVAL -730 day)) AND (date_add(date_add(curdate(), INTERVAL -365 day), INTERVAL -1 second))
      and soc.user_id is not null
      group by 1
    ) a
    inner join order_consolidated soc
    on soc.user_id = a.user_id
    and soc.completed_at between date_add(curdate(), INTERVAL -364 day) AND date_add(curdate(), INTERVAL -1 second)
  ) as revenue_per_0_12,
  (
    # 12-month revenue per 13+ customer
    select sum(soc.total / 100.0) / count(*) as revenue_per_13plus
  # , sum(soc.total/100.0) as revenue
  # , count(*) as users
    from (
      # acquired ast least 1 year prior as of 1 year ago
      select soc.user_id
      from order_consolidated soc
      where completed_at <= date_add(curdate(), interval -730 day)
    ) u
    # their orders in the last year
    left join order_consolidated soc
    on soc.user_id = u.user_id
    and soc.completed_at between date_add(curdate(), INTERVAL -364 day) AND date_add(curdate(), INTERVAL -1 second)
    left join (
      # and didn't buy in the next 12 months
      select soc.user_id
      from order_consolidated soc
      where soc.completed_at between date_add(curdate(), INTERVAL -730 day) AND date_add(curdate(), interval -365 day)
      group by 1
    ) b
    on b.user_id = u.`user_id`
    where b.user_id is null
  ) as revenue_per_13plus
) a
inner join (
  # churned from active to lapsed
  select count(1) as churned_customers
  from (
    select soc.user_id
    , min(soc.completed_at) as first_order_date
    , max(soc.completed_at) as last_order_date
    from order_consolidated soc
    where soc.completed_at between  (date_add(curdate(), INTERVAL -371 DAY)) and (date_add(curdate(), INTERVAL -1 second))
    group by 1
    having first_order_date between date_add(curdate(), INTERVAL -371 DAY) and date_add(curdate(), INTERVAL -364 day)
    and last_order_date between date_add(curdate(), INTERVAL -371 DAY) and date_add(curdate(), INTERVAL -364 day)
  ) a
) ch
) b
; ;;
    persist_for: "12 hours"
  }

  dimension: customers_earned {
    type: number
    sql: ${TABLE}.customers_earned ;;
  }

  dimension: 12_month_average_ltr {
    type: number
    sql: ${TABLE}.12_month_average_ltr ;;
  }

  dimension: 12_month_ltr_earned {
    type: number
    sql: ${TABLE}.12_month_ltr_earned ;;
  }

  dimension: customers_churned {
    type: number
    sql: ${TABLE}.customers_churned ;;
  }

  dimension: 12_month_lost_ltr_per_churn {
    type: number
    sql: ${TABLE}.12_month_lost_ltr_per_churn ;;
  }

  dimension: 12_month_ltr_churned {
    type: number
    sql: ${TABLE}.12_month_ltr_churned ;;
  }

  dimension: 12_month_average_ltr_earned {
    type: number
    sql: ${12_month_ltr_earned} / ${customers_earned} ;;
  }

  dimension: 12_month_net_ltr_earn_churn {
    type: number
    sql: ${12_month_ltr_earned} - ${12_month_ltr_churned} ;;
  }

  dimension: annualized_earn_churn {
    type: number
    sql: (${12_month_ltr_earned} - ${12_month_ltr_churned}) * 52 ;;
  }

  dimension: estimated_ecomm_run_rate {
    type: number
    sql: (${12_month_ltr_earned} - ${12_month_ltr_churned}) * 52 / 0.65 ;;
  }

  measure: count {
    type: count
  }
}
customers_earned_churned_last_7.view.lookml · 146 lines
source-preserved
- view: customers_earned_churned_last_7
  derived_table:
    sql: select
# acq.iov_vs_aov, ltr.average_ltr,
count(*) as customers_earned
, max(ltr.average_ltr) as 12_month_average_ltr
, sum(acq.iov_vs_aov * ltr.average_ltr) as 12_month_ltr_earned
, b.*
from
(
  # Initial OV as % of AOV over last 6 months
  select soc.user_id, sum(soc.total / 100.0) / count(*) /
    (
    select sum(soc.total / 100.0) / count(*)
    from order_consolidated soc
    where soc.completed_at between date_add(curdate(), INTERVAL -6 month) and date_add(curdate(), INTERVAL -1 second)
    ) as iov_vs_aov

  from order_consolidated soc
  where soc.completed_at between (date_add(curdate(), INTERVAL -8 day)) and (date_add(curdate(), INTERVAL -1 second))
  group by 1
) acq
inner join
(
  select sum(12_month_ltr) / count(*) as average_ltr
  from (
    select
    fp.user_id
    , sum(soc.total / 100.0) as 12_month_ltr
  # , soc.completed_at
  # , fp.date_first_purchase
  # , datediff(soc.completed_at, fp.date_first_purchase)
    from

    (
      select soc.user_id, min(completed_at) as date_first_purchase
      from order_consolidated soc
      group by 1
      having date_first_purchase BETWEEN (date_add(curdate(), INTERVAL -730 day)) AND (date_add(date_add(curdate(), INTERVAL -365 day), INTERVAL -1 second))
    ) fp
     left join order_consolidated as soc
        on soc.user_id = fp.user_id
          and datediff(soc.completed_at, fp.date_first_purchase) <= 365
    group by 1

  ) a
) ltr
inner join (
select
ch.churned_customers as customers_churned
, (revenue_per_0_12 - revenue_per_13plus) as 12_month_lost_ltr_per_churn
, ch.churned_customers * (revenue_per_0_12 - revenue_per_13plus) as 12_month_ltr_churned
from (
  # metrics on active vs lapsed
  select
  (
    # 12-month revenue per 0-12 customer
    select sum(soc.total / 100.0) / count(*) as revenue_per_0_12
    from
    (
      select soc.user_id
      from order_consolidated soc
      where soc.completed_at BETWEEN (date_add(curdate(), INTERVAL -730 day)) AND (date_add(date_add(curdate(), INTERVAL -365 day), INTERVAL -1 second))
      and soc.user_id is not null
      group by 1
    ) a
    inner join order_consolidated soc
    on soc.user_id = a.user_id
    and soc.completed_at between date_add(curdate(), INTERVAL -364 day) AND date_add(curdate(), INTERVAL -1 second)
  ) as revenue_per_0_12,
  (
    # 12-month revenue per 13+ customer
    select sum(soc.total / 100.0) / count(*) as revenue_per_13plus
  # , sum(soc.total/100.0) as revenue
  # , count(*) as users
    from (
      # acquired ast least 1 year prior as of 1 year ago
      select soc.user_id
      from order_consolidated soc
      where completed_at <= date_add(curdate(), interval -730 day)
    ) u
    # their orders in the last year
    left join order_consolidated soc
    on soc.user_id = u.user_id
    and soc.completed_at between date_add(curdate(), INTERVAL -364 day) AND date_add(curdate(), INTERVAL -1 second)
    left join (
      # and didn't buy in the next 12 months
      select soc.user_id
      from order_consolidated soc
      where soc.completed_at between date_add(curdate(), INTERVAL -730 day) AND date_add(curdate(), interval -365 day)
      group by 1
    ) b
    on b.user_id = u.`user_id`
    where b.user_id is null
  ) as revenue_per_13plus
) a
inner join (
  # churned from active to lapsed
  select count(1) as churned_customers
  from (
    select soc.user_id
    , min(soc.completed_at) as first_order_date
    , max(soc.completed_at) as last_order_date
    from order_consolidated soc
    where soc.completed_at between  (date_add(curdate(), INTERVAL -371 DAY)) and (date_add(curdate(), INTERVAL -1 second))
    group by 1
    having first_order_date between date_add(curdate(), INTERVAL -371 DAY) and date_add(curdate(), INTERVAL -364 day)
    and last_order_date between date_add(curdate(), INTERVAL -371 DAY) and date_add(curdate(), INTERVAL -364 day)
  ) a
) ch
) b
;
    persist_for: 12 hours
  fields:
  - dimension: customers_earned
    type: number
    sql: ${TABLE}.customers_earned
  - dimension: 12_month_average_ltr
    type: number
    sql: ${TABLE}.12_month_average_ltr
  - dimension: 12_month_ltr_earned
    type: number
    sql: ${TABLE}.12_month_ltr_earned
  - dimension: customers_churned
    type: number
    sql: ${TABLE}.customers_churned
  - dimension: 12_month_lost_ltr_per_churn
    type: number
    sql: ${TABLE}.12_month_lost_ltr_per_churn
  - dimension: 12_month_ltr_churned
    type: number
    sql: ${TABLE}.12_month_ltr_churned
  - dimension: 12_month_average_ltr_earned
    type: number
    sql: ${12_month_ltr_earned} / ${customers_earned}
  - dimension: 12_month_net_ltr_earn_churn
    type: number
    sql: ${12_month_ltr_earned} - ${12_month_ltr_churned}
  - dimension: annualized_earn_churn
    type: number
    sql: (${12_month_ltr_earned} - ${12_month_ltr_churned}) * 52
  - dimension: estimated_ecomm_run_rate
    type: number
    sql: (${12_month_ltr_earned} - ${12_month_ltr_churned}) * 52 / 0.65
  - measure: count
    type: count

Contents

  • customers_earned_churned_last_7.sql
  • customers_earned_churned_last_7.sql
  • customers_earned_churned_last_7.view.lkml
  • customers_earned_churned_last_7.view.lookml