Digital marketing campaign analysis

Below are SQL queries I created for a media campaign analysis which includes click-through rates, conversions, ad spend, return on ad spend, and more:

-- CTR: click_through_rate

SELECT
  placement,
  total_clicks/total_impressions::FLOAT * 100 AS ctr
FROM (
  SELECT 
    placement,
    SUM(spend) total_spend,
    SUM(impressions) total_impressions,
    SUM(clicks) total_clicks
  FROM media
  GROUP BY 1) t
ORDER BY ctr DESC;


-- CPC cost_per_click

SELECT
  total_spend/total_clicks::FLOAT cost_per_click
FROM (
  SELECT
    SUM(spend) total_spend,
    SUM(clicks) total_clicks
  FROM media) t;


-- Conversion_rate for purchases made

SELECT
  source,
  medium,
  purchases_made/carts_added::FLOAT * 100 AS conversion_percent
FROM (
  SELECT 
    source,
    medium,
    SUM(add_to_cart) carts_added,
    SUM(purchase) purchases_made
  FROM conversions
  GROUP BY 1,2 ) t
ORDER BY conversion_percent DESC;


-- Cost_per_conversion total spend / total conversion

WITH total_spend AS (
  SELECT
    SUM(spend) sum_spend
  FROM media
),
total_conversions AS (
  SELECT
    SUM(purchase) sum_purchases
  FROM conversions
  WHERE revenue > 0
)
SELECT
  sum_spend,
  sum_purchases,
  sum_spend/sum_purchases::FLOAT AS cost_per_conversion
FROM total_spend
JOIN total_conversions
  ON 1=1;


-- cost per conversion by campaign

WITH total_spend AS (
  SELECT
    CONCAT_WS(' | ', platform, placement) AS platform_placement,
    SUM(spend) sum_spend
  FROM media
  GROUP BY 1
),
total_conversions AS (
  SELECT
    source_medium,
    SUM(purchase) sum_purchases
  FROM conversions
  WHERE revenue > 0
  GROUP BY 1
)
SELECT
  ts.platform_placement,
  ts.sum_spend/tc.sum_purchases::FLOAT AS cost_per_conversion
FROM total_spend ts
JOIN total_conversions tc
  ON ts.platform_placement = tc.source_medium
ORDER BY cost_per_conversion ASC;


-- ROAS Return on ad spend

WITH total_cost AS (
  SELECT
    CONCAT_WS(' | ', platform, placement) AS platform_placement,
    SUM(spend) AS total_spend
  FROM media
  GROUP BY 1
),
total_revenue AS (
  SELECT 
    source_medium,
    SUM(revenue) sum_revenue
  FROM conversions
  WHERE revenue > 0
  GROUP BY 1
)
SELECT
  platform_placement,
  sum_revenue/total_spend::FLOAT * 100 AS return_on_spend
FROM total_cost tc
JOIN total_revenue tr
  ON tc.platform_placement = tr.source_medium
ORDER BY return_on_spend DESC;


-- Cart_Abandon_rate

SELECT
  source_medium,
  (1 - purchases/carts_added::FLOAT) * 100 AS cart_abandon_rate
FROM (
  SELECT
    source_medium,
    SUM(add_to_cart) carts_added,
    SUM(purchase) purchases 
  FROM conversions
  GROUP BY 1) t
ORDER BY cart_abandon_rate;


-- avg spend by campaign

SELECT
  platform_placement,
  AVG(sum_spend) AS avg_spend
FROM (
  SELECT
    CONCAT_WS(' | ', platform, placement) AS platform_placement,
    SUM(spend) sum_spend
  FROM media
  GROUP BY 1) t
GROUP BY 1
ORDER BY avg_spend DESC;


-- How to JOIN into one table?

SELECT 
  *
FROM media2 m
JOIN conversions c
  ON m.platform_placement = c.source_medium
LIMIT 5;


-- click-through rate by month

SELECT
  TO_CHAR(date, 'MON') as month,
  --EXTRACT('month' FROM date::DATE) AS month,
  SUM(clicks)/SUM(impressions)::FLOAT * 100 AS click_through_rate
FROM media
GROUP BY 1;


-- cost per click by month

SELECT
  TO_CHAR(date, 'MON') as month,
  SUM(spend)/SUM(clicks)::FLOAT * 100 AS cost_per_click
FROM media
GROUP BY 1;


-- cost per conversion by month

WITH month_spend AS (
  SELECT
    TO_CHAR(date, 'MON') as month,
    SUM(spend) AS total_spend
  FROM media
  GROUP BY 1
),
month_conversions AS (
  SELECT
    TO_CHAR(date, 'MON') as month,
    SUM(purchase) sum_conversions
  FROM conversions
  WHERE revenue > 0
  GROUP BY 1
)
SELECT
  ms.month,
  total_spend/sum_conversions::FLOAT AS cost_per_Conversion
FROM month_spend ms
JOIN month_conversions mc
  ON ms.month = mc.month;


-- Conversion rate by month

WITH month_clicks AS (
  SELECT
    TO_CHAR(date, 'MON') as month,
    SUM(clicks) AS total_clicks
  FROM media
  GROUP BY 1
),
month_conversions AS (
  SELECT
    TO_CHAR(date, 'MON') as month,
    SUM(purchase) sum_conversions
  FROM conversions
  WHERE revenue > 0
  GROUP BY 1
)
SELECT
  cs.month,
  sum_conversions/total_clicks::FLOAT * 100 AS conversion_rate
FROM month_clicks cs
JOIN month_conversions cn
  ON cs.month = cn.month;


-- revenue by month

SELECT
   TO_CHAR(date, 'MON') as month,
  SUM(revenue)
FROM conversions
WHERE revenue > 0
GROUP BY 1;


-- ROAS by month

WITH revenue_month AS (
  SELECT
    TO_CHAR(date, 'MON') as month,
    SUM(revenue) AS total_revenue
  FROM conversions
  WHERE revenue > 0
  GROUP BY 1
),
total_cost AS (
  SELECT 
    TO_CHAR(date, 'MON') as month,
    SUM(spend) AS total_spend
  FROM media
  GROUP BY 1
)
SELECT
  *,
  total_revenue - total_spend AS difference,
  total_revenue/total_spend::FLOAT AS roas
FROM revenue_month rm
JOIN total_cost tc
  ON rm.month = tc.month;


-- cart abandon rate by month

SELECT
  month,
  (1 - purchases/carts_added::FLOAT) * 100 AS cart_abandon_rate
FROM (
  SELECT
    TO_CHAR(date, 'MON') as month,
    SUM(add_to_cart) carts_added,
    SUM(purchase) purchases 
  FROM conversions
  GROUP BY 1) t
ORDER BY cart_abandon_rate;


-- rCPM revenue generated per 1000 impressions

WITH total_revenue AS (
  SELECT
    TO_CHAR(date, 'MON') as month,
    SUM(revenue) sum_revenue
  FROM conversions
  WHERE revenue > 0
  GROUP BY 1
),
total_impressions AS (
  SELECT
    TO_CHAR(date, 'MON') as month,
    SUM(impressions) AS sum_impressions
  FROM media
  GROUP BY 1
)
SELECT
  ti.month,
  sum_revenue/sum_impressions::FLOAT * 1000 AS rcpm
FROM total_revenue tr
JOIN total_impressions ti
  ON tr.month = ti.month;


-- CPM cost per 1000 impressions

SELECT
  TO_CHAR(date, 'MON') as month,
  SUM(spend)/SUM(impressions) * 1000 AS cost_per_1000
FROM media
GROUP BY 1;

-- performance by month
WITH month_media AS (
  SELECT 
    TO_CHAR(date, 'MON') AS month,
    SUM(spend) AS total_cost,
    SUM(clicks) AS total_clicks,
    SUM(impressions) AS total_impressions
  FROM media
  GROUP BY 1
),
month_conversions AS (
  SELECT 
    TO_CHAR(date, 'MON') AS month,
    SUM(add_to_cart) AS total_carts_added,
    SUM(purchase) AS total_purchases,
    SUM(revenue) AS total_revenue
  FROM conversions
  GROUP BY 1
)
SELECT
  *,
  total_cost / total_clicks::FLOAT AS cpc_cpa,
  total_cost / total_impressions::FLOAT * 1000 AS cpm,
  total_clicks / total_impressions::FLOAT * 100 AS ctr,
  total_revenue / total_cost::FLOAT AS roas,
  total_revenue / total_impressions::FLOAT * 1000 AS rcpm,
  (total_revenue - total_cost) / total_cost::FLOAT * 100 AS roi,
  total_purchases / total_clicks::FLOAT * 100 AS conversion_rate,
  (1 - total_purchases / total_carts_added::FLOAT) * 100 AS cart_abandon_rate,
  (total_revenue - LEAD(total_revenue) OVER ()) / LEAD(total_revenue::FLOAT) OVER () * 100 AS revenue_percent_change
FROM month_media m
JOIN month_conversions c
  ON m.month = c.month;