Overview
In a fast-paced marketing environment, marketers and campaign managers must collaborate to optimize customer engagement and maximize conversion rates. Measuring the impact of each new marketing campaign quickly and accurately is challenging, which can lead to missed opportunities. Being able to promptly determine whether or not a campaign was effective allows the business to adjust marketing strategies dynamically in response to customer behavior.
Real-time processing and analysis of customer engagement data enables analysts to monitor campaign performance as it happens. They can determine what strategies are working and adapt based on emerging trends. This approach improves ROI and ensures marketing efforts are appealing to customer preferences.
In this tutorial, you will learn how to analyze the effects of a market campaign in real-time.
Prerequisites
- Ensure that the PostgreSQL interactive terminal,
psql
, is installed in your environment. For detailed instructions, see Download PostgreSQL.
- Install and run RisingWave. For detailed instructions on how to quickly get started, see the Quick start guide.
- Ensure that a Python environment is set up and install the psycopg2 library.
Step 1: Set up the data source tables
Once RisingWave is installed and deployed, run the three SQL queries below to set up the tables. You will insert data into these tables to simulate live data streams.
-
The marketing_events
table tracks user interactions with marketing campaigns and includes details, such as the number of clicks and impressions, of each event.
CREATE TABLE marketing_events (
event_id varchar PRIMARY KEY,
user_id integer,
campaign_id varchar,
channel_type varchar, -- email, social, search, display
event_type varchar, -- impression, click, conversion
amount numeric, -- conversion amount if applicable
utm_source varchar,
utm_medium varchar,
utm_campaign varchar,
timestamp timestamptz DEFAULT CURRENT_TIMESTAMP
);
-
The campaigns
table stores information about each marketing campaign, such as what type of campaign it was and the target audience.
CREATE TABLE campaigns (
campaign_id varchar PRIMARY KEY,
campaign_name varchar,
campaign_type varchar, -- regular, ab_test
start_date timestamptz,
end_date timestamptz,
budget numeric,
target_audience varchar
);
-
The ab_test_variants
contains details, such as the variant type and name, about A/B test variations for campaigns.
CREATE TABLE ab_test_variants (
variant_id varchar PRIMARY KEY,
campaign_id varchar,
variant_name varchar, -- A, B, Control
variant_type varchar, -- subject_line, creative, landing_page
content_details varchar
);
Step 2: Run the data generator
To keep this demo simple, a Python script is used to generate and insert data into the tables created above.
Clone the awesome-stream-processing repository.
git clone https://github.com/risingwavelabs/awesome-stream-processing.git
Navigate to the market_analysis folder.
cd awesome-stream-processing/tree/main/02-simple-demos/e_commerce/marketing_analysis
Run the data_generator.py
file. This Python script utilizes the psycopg2
library to establish a connection with RisingWave so you can generate and insert synthetic data into the three tables described above.
If you are not running RisingWave locally or using default credentials, update the connection parameters accordingly:
default_params = {
"dbname": "dev",
"user": "root",
"password": "",
"host": "localhost",
"port": "4566"
}
Step 3: Create materialized views
In this demo, you will create multiple materialized views to investigate the efficacy of marketing campaigns.
Materialized views contain the results of a view expression and are stored in the RisingWave database. The results of a materialized view are computed incrementally and updated whenever new events arrive and do not require to be refreshed. When you query from a materialized view, it will return the most up-to-date computation results.
Evaluate campaign metrics
The campaign_performance
materialized view summarizes the performance metrics of the marketing campaigns over 1-hour time windows. The tumble
function is used to map each event into a 1-hour time window.
You will gain insight into key performance indicators for each campaign, such as the number of impressions, clicks, and conversions.
CREATE MATERIALIZED VIEW campaign_performance AS
SELECT
window_start,
window_end,
c.campaign_id,
c.campaign_name,
c.campaign_type,
COUNT(DISTINCT CASE WHEN event_type = 'impression' THEN me.event_id END) as impressions,
COUNT(DISTINCT CASE WHEN event_type = 'click' THEN me.event_id END) as clicks,
COUNT(DISTINCT CASE WHEN event_type = 'conversion' THEN me.event_id END) as conversions,
SUM(CASE WHEN event_type = 'conversion' THEN amount ELSE 0 END) as revenue,
COUNT(DISTINCT CASE WHEN event_type = 'click' THEN me.event_id END)::float /
NULLIF(COUNT(DISTINCT CASE WHEN event_type = 'impression' THEN me.event_id END), 0) as ctr,
COUNT(DISTINCT CASE WHEN event_type = 'conversion' THEN me.event_id END)::float /
NULLIF(COUNT(DISTINCT CASE WHEN event_type = 'click' THEN me.event_id END), 0) as conversion_rate
FROM TUMBLE(marketing_events, timestamp, INTERVAL '1 HOUR') as me
JOIN campaigns c ON me.campaign_id = c.campaign_id
GROUP BY
window_start,
window_end,
c.campaign_id,
c.campaign_name,
c.campaign_type;
You can query from campaign_performance
to see the results.
SELECT * FROM campaign_performance LIMIT 5;
window_start | window_end | campaign_id | campaign_name | campaign_type | impressions | clicks | conversions | revenue | ctr | conversion_rate
---------------------------+---------------------------+---------------+---------------+---------------+-------------+--------+-------------+----------+--------------------+--------------------
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | camp_04733bc2 | Campaign 7 | regular | 114 | 149 | 125 | 33242.69 | 1.3070175438596492 | 0.8389261744966443
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | camp_3d30ac7d | Campaign 4 | regular | 173 | 160 | 149 | 42910.78 | 0.9248554913294798 | 0.93125
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | camp_32072343 | Campaign 2 | ab_test | 154 | 139 | 138 | 36509.81 | 0.9025974025974026 | 0.9928057553956835
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | camp_8d14a26f | Campaign 9 | ab_test | 127 | 146 | 155 | 44141.92 | 1.1496062992125984 | 1.0616438356164384
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | camp_93d85763 | Campaign 10 | ab_test | 161 | 119 | 158 | 42155.77 | 0.7391304347826086 | 1.3277310924369747
Analyze marketing channels
The channel_attribution
materialized view analyzes the marketing performance over 1-hour time windows. Again, the tumble
function is used to map each event into a 1-hour time window. Then, you group by the time window and channel type to find the aggregate channel metrics.
This materialized view helps to show the effectiveness of each marketing channel in driving conversion and revenue. It provides details such as the number of unique, engaged users, and the total revenue generated by each channel.
CREATE MATERIALIZED VIEW channel_attribution AS
SELECT
window_start,
window_end,
channel_type,
utm_source,
utm_medium,
COUNT(DISTINCT user_id) as unique_users,
COUNT(DISTINCT CASE WHEN event_type = 'conversion' THEN event_id END) as conversions,
SUM(CASE WHEN event_type = 'conversion' THEN amount ELSE 0 END) as revenue,
SUM(CASE WHEN event_type = 'conversion' THEN amount ELSE 0 END) /
NULLIF(COUNT(DISTINCT CASE WHEN event_type = 'conversion' THEN event_id END), 0) as avg_order_value
FROM TUMBLE(marketing_events, timestamp, INTERVAL '1 HOUR')
GROUP BY
window_start,
window_end,
channel_type,
utm_source,
utm_medium;
You can query from channel_attribution
to see the results.
SELECT * FROM channel_attribution LIMIT 5;
window_start | window_end | channel_type | utm_source | utm_medium | unique_users | conversions | revenue | avg_order_value
---------------------------+---------------------------+--------------+------------+------------+--------------+-------------+---------+--------------------------------
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | display | email | organic | 49 | 20 | 5534.39 | 276.7195
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | display | linkedin | email | 49 | 18 | 5004.84 | 278.04666666666666666666666667
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | display | linkedin | organic | 55 | 15 | 4298.15 | 286.54333333333333333333333333
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | display | linkedin | social | 51 | 15 | 4523.88 | 301.5920
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | email | google | email | 46 | 18 | 5315.16 | 295.28666666666666666666666667
Assess A/B tests
The ab_test_results
materialized view analyzes the results of the A/B test over 1-hour time windows. A multi-way join is used to retrieve campaign details and details on the test variations.
This materialized view allows you to evaluate the A/B test and determine which test variant performed best. From there, you can make more informed decisions on which marketing campaign to move forward with.
CREATE MATERIALIZED VIEW ab_test_results AS
SELECT
window_start,
window_end,
c.campaign_id,
c.campaign_name,
av.variant_name,
av.variant_type,
COUNT(DISTINCT CASE WHEN event_type = 'impression' THEN me.event_id END) as impressions,
COUNT(DISTINCT CASE WHEN event_type = 'click' THEN me.event_id END) as clicks,
COUNT(DISTINCT CASE WHEN event_type = 'conversion' THEN me.event_id END) as conversions,
SUM(CASE WHEN event_type = 'conversion' THEN amount ELSE 0 END) as revenue,
COUNT(DISTINCT CASE WHEN event_type = 'conversion' THEN me.event_id END)::float /
NULLIF(COUNT(DISTINCT CASE WHEN event_type = 'click' THEN me.event_id END), 0) as conversion_rate
FROM TUMBLE(marketing_events, timestamp, INTERVAL '1 HOUR') as me
JOIN campaigns c ON me.campaign_id = c.campaign_id
JOIN ab_test_variants av ON c.campaign_id = av.campaign_id
WHERE c.campaign_type = 'ab_test'
GROUP BY
window_start,
window_end,
c.campaign_id,
c.campaign_name,
av.variant_name,
av.variant_type;
You can query from ab_test_results
to see the results.
SELECT * FROM ab_test_results LIMIT 5;
window_start | window_end | campaign_id | campaign_name | variant_name | variant_type | impressions | clicks | conversions | revenue | conversion_rate
---------------------------+---------------------------+---------------+---------------+--------------+--------------+-------------+--------+-------------+----------+--------------------
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | camp_93d85763 | Campaign 10 | A | creative | 213 | 166 | 207 | 55158.84 | 1.2469879518072289
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | camp_93d85763 | Campaign 10 | B | creative | 213 | 166 | 207 | 55158.84 | 1.2469879518072289
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | camp_93d85763 | Campaign 10 | Control | landing_page | 213 | 166 | 207 | 55158.84 | 1.2469879518072289
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | camp_4537f3d7 | Campaign 3 | A | landing_page | 160 | 191 | 165 | 45763.74 | 0.8638743455497382
2024-12-20 00:00:00+00:00 | 2024-12-20 01:00:00+00:00 | camp_4537f3d7 | Campaign 3 | B | creative | 160 | 191 | 165 | 45763.74 | 0.8638743455497382
When finished, press Ctrl+C
to close the connection between RisingWave and psycopg2
.
Summary
In this tutorial, you learn:
- How to evaluate an A/B test in real time.