Skip to content

Repository files navigation

Marketing Campaign Exploratory Data Analysis (EDA) Using MySQL


๐Ÿ’ผ Business Problem

  • Businesses invest significantly in paid marketing campaigns across multiple digital channels to attract customers and drive revenue. However, high advertising spend and website traffic do not always translate into successful conversions or profitable outcomes. Without analyzing campaign performance, customer behavior, and conversion metrics, marketing teams struggle to identify which campaigns deliver the best results and where budget is being wasted.

    This project addresses that challenge by analyzing paid marketing data to evaluate campaign effectiveness, traffic quality, customer acquisition, conversion rates, and return on ad spend (ROAS). The insights generated help businesses optimize marketing strategies, allocate budgets more effectively, improve campaign performance, and make data-driven decisions that maximize business growth.

๐ŸŽฏ Project Objective

  • The objective of this project is to analyze the effectiveness of paid marketing campaigns by leveraging SQL to transform raw marketing data into actionable business insights. The analysis focuses on evaluating campaign performance, website traffic, customer acquisition, conversion rates, advertising spend, and revenue generation to measure overall marketing efficiency.

    By connecting campaign, traffic, customer, and order data, this project identifies high-performing marketing channels, uncovers opportunities to optimize conversion funnels, evaluates Return on Ad Spend (ROAS), and supports data-driven decision-making for improving marketing performance and maximizing business growth.

๐Ÿ—„๏ธ Database Overview

The project consists of four connected datasets representing the complete customer journey.

Table Description
Campaign Marketing campaign information including platform, targeting strategy, and advertising spend
Traffic Website visitor sessions, bounce behavior, page views, and devices
Customers Customer registration information
Orders Purchase transactions, discounts, returns, refunds, and revenue

๐Ÿ”— Database Relationship

Campaign โ–ถ Traffic โ–ถ Customers โ–ถ Orders


1 Timeline Check: What is the exact start and end date of our data?

  SELECT
  	MIN(DATE(Order_Timestamp)) AS FirstDate,
  	MAX(DATE(Order_Timestamp)) AS LastDate,
    DATEDIFF(MAX(DATE(Order_Timestamp)), MIN(DATE(Order_Timestamp))) AS Duration
  FROM Orders;

2. KPIs Total Ad Spend, Total Traffic, Total Bounce, Conversion rate, Total Customers, Total Orders

  • Using (Scaler Sub Query)
SELECT 
-- 1. Total Ad Spend
    (SELECT ROUND(SUM(Ad_Spend_USD), 2) FROM Campaign) AS Total_Ad_Spend_USD,
    
    -- 2. Total Traffic (Sessions)
    (SELECT COUNT(SessionID) FROM Traffic) AS Total_Traffic,
    
    -- 3. Total Bounces (People who left immediately)
    (SELECT SUM(Bounce_Flag) FROM Traffic) AS Total_Bounces,
    
    -- 4. Conversion Rate (Orders divided by Traffic)
    ROUND(
        ( (SELECT COUNT(OrderID) FROM Orders) / 
          (SELECT COUNT(SessionID) FROM Traffic) ) * 100
    , 2) AS Conversion_Rate_Pct,
    
    -- 5. Total Customers (Unique accounts created)
    (SELECT COUNT(DISTINCT CustomerID) FROM Customers) AS Total_Customers,
    
    -- 6. Total Orders Placed
    (SELECT COUNT(OrderID) FROM Orders) AS Total_Orders;

3. Analyze the top performing Ads Campaign platform in term of

  • Total Traffic, Total Bounce, Conversion rate, Total Customers, Total Orders, Total Kept, Returned and Refunded Orders
SELECT
	cm.Platform,
    COUNT(t.SessionID) AS Traffic,
    SUM(t.Bounce_Flag) AS Total_Bounce,
    SUM(CASE WHEN t.Pages_Viewed > 1 THEN '1' ELSE '0' END) AS Conversion,
    COUNT(DISTINCT o.CustomerID) AS Total_Customers,
    COUNT(o.OrderID) AS Total_Order,
    SUM(CASE WHEN Return_Status = 'Kept' THEN '1' ELSE '0' END) AS Total_Delivered,
    SUM(CASE WHEN Return_Status IN ('Returned', 'Refunded') THEN '1' ELSE '0' END) AS Return_Refunded
FROM Campaign cm
JOIN Traffic t ON cm.CampaignID = t.CampaignID
LEFT JOIN Orders o ON t.SessionID = o.SessionID
GROUP BY cm.Platform
ORDER BY Total_Delivered DESC;

4. Oraginc Vs Paid Orders

  • Total Traffic, Total Bounce, Conversion rate, Total Customers, Total Orders, Total Kept, Returned and Refunded Orders
SELECT
	(CASE WHEN t.CampaignID IS NULL THEN 'Organic Traffic' ELSE 'Paid Traffic' END) AS Traffic,
	COUNT(t.SessionID) AS Total_Traffic,
    SUM(t.Bounce_Flag) AS Total_Bounce,
    SUM(CASE WHEN t.Pages_Viewed > 1 THEN '1' ELSE '0' END) AS Total_Conversion,
    COUNT(DISTINCT o.CustomerID) AS Unique_Cust,
    COUNT(o.OrderID) AS Total_Order,
    SUM(o.Cart_Value) AS Revenue,
    SUM(CASE WHEN Return_Status = 'Kept' THEN 1 ELSE 0 END) AS Total_Delivered,
    SUM(CASE WHEN Return_Status IN ('Returned', 'Refunded') THEN 1 ELSE 0 END) AS Return_Refunded
FROM Traffic t
LEFT JOIN Orders o ON t.SessionID = o.SessionID
GROUP BY (CASE WHEN t.CampaignID IS NULL THEN 'Organic Traffic' ELSE 'Paid Traffic' END);

Understanding who is visiting the site and how much it costs to get them there.

5. Cost Per Click (CPC) Proxy: Which ad platform drives the most site visits per dollar spent?

  • Using Sub_Query Method
-- Main_Query
SELECT
	platform,
    SUM(Ad_Spend_USD) AS Ad_Spend,
    SUM(t.Campaign_Click) AS Campaign_Click,
    ROUND(SUM(Ad_Spend_USD) / SUM(t.Campaign_Click), 2) AS Cost_Per_Click_CPC,
    SUM(t.Campaign_Click) / SUM(Ad_Spend_USD)  AS Visits_Per_Dollar
   FROM Campaign c
LEFT JOIN 

-- Sub_Query
	(SELECT
    CampaignID,
	COUNT(SessionID) AS Campaign_Click
    FROM Traffic t
    WHERE CampaignID IS NOT NULL
    GROUP BY CampaignID) t
ON c.CampaignID = t.CampaignID
GROUP BY c.platform;
  • Using CTE Method
-- Sum Ad_Spend on different platform
WITh Platform_budgets AS (
	SELECT
		Platform,
		SUM(Ad_Spend_USD) AS Ad_Spend
	FROM Campaign
    GROUP BY Platform),
    
-- Count total traffic and click 
 Platform_Traffic AS (
	SELECT
		c.Platform,
		COUNT(t.SessionID) AS Campaign_Click
    FROM Campaign c
JOIN Traffic t
ON t.CampaignID = c.CampaignID
GROUP BY c.Platform)

-- Merge two table to show the complete result
SELECT 
	b.Platform,
    b.Ad_Spend,
    t.Campaign_Click,
    ROUND(b.Ad_Spend / t.Campaign_Click, 2) AS Cost_Per_Click_CPC,
    ROUND(t.Campaign_Click / b.Ad_Spend, 2) AS Visit_Per_USD
FROM Platform_budgets b
JOIN Platform_Traffic t
ON b.Platform = t.Platform;

6. Campaign Fatigue (Bounce Rates): Which specific campaigns have a Bounce Rate higher than 60%, indicating we are targeting the wrong audience?

SELECT
    c.Platform,
	SUM(CASE WHEN t.Bounce_Flag = 1 THEN '1' ELSE 0 END) AS Bounce,
    COUNT(t.SessionID) AS Traffic,
    COUNT(t.SessionID) / SUM(CASE WHEN t.Bounce_Flag = 1 THEN '1' ELSE 0 END) * 100 AS Bounce_Rate,
    SUM(CASE WHEN t.Bounce_Flag = 1 THEN '1' ELSE 0 END) / COUNT(t.SessionID) * 100 AS B_Rate
FROM Traffic t
JOIN Campaign c
ON t.CampaignID = c.CampaignID
GROUP BY c.Platform;

7. Traffic Quality: What is the average number of pages viewed per session, segmented by the device the user is on?

SELECT
	Device,
	SessionID,
    AVG(Pages_Viewed) OVER ( PARTITION BY Device ) AS Avg_Page_Visit
FROM Traffic
GROUP BY Device,
	SessionID
ORDER BY AVG(Pages_Viewed) DESC;

8. High-Intent Window Shoppers: How many users visited more than 4 pages but never placed an order?

-- This Showing all the result of Paid campaign
WITH no_order AS (
SELECT
	CustomerID,
    Pages_Viewed
FROM traffic
WHERE Pages_Viewed > 4 AND CampaignID IS NOT NULL AND
	CustomerID NOT IN (SELECT CustomerID FROM Orders))
    
SELECT COUNT( DISTINCT CustomerID) AS Lost_Customers FROM no_order;
  • This Query is Showing all the customer who come from organic and who come fro paid campaign who visit more than 4 pages but didn't order
WITH null_Order AS (
SELECT
	t.CustomerID,
    t.Pages_Viewed
FROM Traffic t
LEFT JOIN Orders o 
ON t.CustomerID = o.CustomerID
WHERE t.Pages_Viewed > 4 AND
o.OrderID IS NULL)

SELECT COUNT(DISTINCT CustomerID) AS Lost_Orders FROM null_order;

9. Understanding why people buy (or why they leave). The Device Funnel: What is the exact Session-to-Order conversion rate for Mobile vs. Desktop vs. Tablet?

SELECT
	t.Device,
    COUNT(t.SessionID) AS Total_Traffic,
    COUNT(o.OrderID) AS Total_Orders,
    ROUND(COUNT(o.OrderID) / COUNT(t.SessionID) * 100, 2) AS Conversion_Rate
FROm Traffic t
LEFT JOIN Orders o
ON t.SessionID = o.SessionID
GROUP BY Device;

10. Promo Code Dependency: What percentage of our total orders relied on a discount code?

SELECT
	CASE WHEN Discount_Code IS NULL THEN 'Organic' ELSE 'Discounted' END AS Order_Type,
    COUNT(OrderID) AS Orders,
    (SELECT COUNT(OrderID) FROM Orders) AS Total_Order,
    ROUND(COUNT(OrderID) / (SELECT COUNT(OrderID) FROM Orders) * 100, 2) AS Conversion_Rate
FROM Orders
GROUP BY CASE WHEN Discount_Code IS NULL THEN 'Organic' ELSE 'Discounted' END;

11. Time-to-Activation: How many days, on average, does it take for a newly signed-up user to make their first website visit?

SELECT
	DISTINCT c.CustomerID,
    c.SignupDate,
    DATE(MIN(o.Order_Timestamp)) AS Fisrt_Purchase,
    DATEDIFF(DATE(MIN(o.Order_Timestamp)), c.SignupDate) AS Duration
FROM Customers c
JOIN Orders o
ON c.CustomerID = o.CustomerID
GROUP BY c.CustomerID
ORDER BY DATEDIFF(DATE(MIN(o.Order_Timestamp)), c.SignupDate);

12. True Return on Ad Spend (ROAS): What is the exact ROAS multiplier for Meta, Google, and TikTok, strictly excluding refunded items?

-- Calculating Platform Ad Spend
WITH Platform_Spend AS (
SELECT
	Platform,
    SUM(Ad_Spend_USD) AS Ad_Spend
FROM Campaign
GROUP BY Platform),

-- Calculating platform Net Revenue
Platform_Revenue AS (
SELECT
	c.Platform,
    SUM(o.Cart_Value) AS Gross_Revenue,
    SUM(CASE WHEN Return_Status = 'Kept' THEN o.Cart_Value ELSE 0 END) AS Net_Revenue
FROM Campaign c
JOIN Traffic t
ON  c.CampaignID = t.CampaignID
JOIN Orders o ON
t.SessionID = o.SessionID
GROUP BY c.Platform)

-- Merge Both CTE Table to get Final result
SELECT
	s.Platform,
    s.Ad_Spend,
    r.Net_Revenue,
    r.Gross_Revenue,
    r.Net_Revenue - s.Ad_Spend AS ROAS1,
    s.Ad_Spend / r.Net_Revenue AS ROAS
FROM Platform_Spend s
JOIN Platform_Revenue r
ON s.Platform = r.Platform
GROUP BY s.Platform;

13. Targeting Profitability: Which ad targeting strategy (Lookalike, Retargeting, Broad) generates the highest net revenue?

-- Calculating total campaign and spend
WITH Targeting_Spend AS (
SELECT
	Targeting_Type,
    COUNT(CampaignID) AS Total_Campaign,
    SUM(Ad_Spend_USD) AS Ad_Spend
FROM Campaign
GROUP BY Targeting_Type),

-- Calculating Revenue, Orders by targeting Types
Targeting_Rev AS (
SELECT
	c.Targeting_Type,
    SUM(CASE WHEN o.Return_Status = 'Kept' THEN 1  ELSE 0 END) AS Total_Order,
    SUM(CASE WHEN o.Return_Status = 'Kept' THEN o.Cart_Value ELSE 0 END) AS Net_Revenue
FROM Campaign c
JOIN Traffic t ON c.CampaignID = t.CampaignID
JOIN Orders o ON o.SessionID = t.SessionID
GROUP BY c.Targeting_Type)

-- Combine both to get final result Total Campaign, Ad Spend, Orders and net revenue
SELECT
	s.Targeting_Type,
    s.Total_Campaign,
    s.Ad_Spend,
    r.Total_Order,
    r.Net_Revenue
FROM Targeting_Spend s
JOIN Targeting_Rev r ON s.Targeting_Type = r.Targeting_Type
ORDER BY r.Net_Revenue DESC;

14. Predicting the future of the business using Window Functions.

Month-over-Month (MoM) Growth: What is our rolling 30-day gross revenue trend over the dataset's lifespan?

SELECT 
        DATE_FORMAT(Order_Timestamp, '%Y-%m') AS Sales_Month,
        SUM(Cart_Value) AS Current_Month_Revenue,
        LAG(SUM(Cart_Value)) OVER ( ORDER BY DATE_FORMAT(Order_Timestamp, '%Y-%m')) AS Pre_Month,
         SUM(Cart_Value) - LAG(SUM(Cart_Value)) OVER ( ORDER BY DATE_FORMAT(Order_Timestamp, '%Y-%m')) AS Diff,
         ((SUM(Cart_Value) - LAG(SUM(Cart_Value)) OVER ( ORDER BY DATE_FORMAT(Order_Timestamp, '%Y-%m'))) / 
         LAG(SUM(Cart_Value)) OVER ( ORDER BY DATE_FORMAT(Order_Timestamp, '%Y-%m'))) * 100 AS MoM_Growth
    FROM Orders 
    GROUP BY DATE_FORMAT(Order_Timestamp, '%Y-%m')
    ORDER BY DATE_FORMAT(Order_Timestamp, '%Y-%m');
  • Another Query Using CTEs
WITH Monthly_Sales AS (
		SELECT
			DATE_FORMAT(Order_Timestamp, '%Y-%m' ) AS Sales_Month,
			SUM(Cart_Value) AS Current_Month_Rev
		FROM Orders
        GROUP BY DATE_FORMAT(Order_Timestamp, '%Y-%m' )
	),
    
MoM_Comparision AS (
		SELECT
        	Sales_Month,
        	Current_Month_Rev,
        	LAG(Current_Month_Rev) OVER ( ORDER BY Sales_Month) AS Pre_Month_Rev
    FROM Monthly_Sales
	)
    
    SELECT
        Sales_Month,
        Current_Month_Rev,
        Pre_Month_Rev,
        ROUND(
        ((Current_Month_Rev - Pre_Month_Rev) / Pre_Month_rev) * 100, 2) AS MoM_Change
    FROM MoM_Comparision
        WHERE Pre_Month_Rev IS NOT NULL
        ORDER BY Sales_Month;

๐Ÿ“š SQL Concepts Covered


๐Ÿ› ๏ธ Skills & Technologies Used


About

Marketing Campaign Analysis Using MySQL

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors