AD/Marketing Tech
Use streaming SQL to recalibrate bids, compute multi-touch attribution, and reallocate budgets as campaign performance shifts.
Trusted by 1,000+ Data-Driven Organizations
for Real-time Analytics
The Problem
Hourly or daily reporting means your bids, budgets, and attribution models are always behind the market. By the time you see a problem, budget has already been wasted on underperforming placements.
With RisingWave
RisingWave processes impression, click, and conversion events as they arrive. Write streaming SQL that continuously updates bid models, computes attribution, and triggers budget reallocation in milliseconds.
A DSP (demand-side platform) processes 2M bid requests/second across 15 ad exchanges. Each bid decision must happen in under 100ms. The ML model's bid price needs continuous recalibration based on win rates, CPAs, and remaining campaign budgets.
| bid_id | campaign_id | exchange | bid_price | won | impression | click | conversion | ts |
|---|---|---|---|---|---|---|---|---|
| bid_9a3f01 | CAMP-8812 | google_adx | 2.4 | true | true | false | false | 2024-03-15T14:00:01.112Z |
| bid_9a3f02 | CAMP-8812 | google_adx | 2.55 | true | true | true | true | 2024-03-15T14:00:01.445Z |
| bid_9a3f03 | CAMP-8812 | openx | 2.2 | false | false | false | false | 2024-03-15T14:00:01.667Z |
| bid_9a3f04 | CAMP-8812 | pubmatic | 2.8 | true | true | true | false | 2024-03-15T14:00:02.001Z |
| bid_7b2e01 | CAMP-4455 | google_adx | 1.9 | true | true | false | false | 2024-03-15T14:00:01.230Z |
| bid_7b2e02 | CAMP-4455 | index_exchange | 1.75 | true | true | true | true | 2024-03-15T14:00:01.890Z |
CREATE SOURCE bid_events WITH (
connector = 'kafka',
topic = 'dsp.bid_events',
properties.bootstrap.server = 'broker:9092'
) FORMAT PLAIN ENCODE JSON;
CREATE MATERIALIZED VIEW campaign_metrics AS
SELECT
campaign_id,
window_start,
COUNT(*) AS total_bids,
COUNT(*) FILTER (WHERE won) AS wins,
ROUND(
COUNT(*) FILTER (WHERE won)::NUMERIC
/ NULLIF(COUNT(*), 0) * 100, 1
) AS win_rate_pct,
COUNT(*) FILTER (WHERE click) AS clicks,
ROUND(
COUNT(*) FILTER (WHERE click)::NUMERIC
/ NULLIF(COUNT(*) FILTER (WHERE impression), 0) * 100, 2
) AS ctr_pct,
COUNT(*) FILTER (WHERE conversion) AS conversions,
CASE
WHEN COUNT(*) FILTER (WHERE conversion) = 0 THEN NULL
ELSE ROUND(
SUM(bid_price) FILTER (WHERE won)::NUMERIC
/ COUNT(*) FILTER (WHERE conversion), 2
)
END AS effective_cpa,
ROUND(AVG(bid_price) FILTER (WHERE won), 2) AS avg_win_price
FROM TUMBLE(bid_events, ts, INTERVAL '5 MINUTES')
GROUP BY campaign_id, window_start;| campaign_id | current_cpa | target_cpa | deviation_pct | bid_modifier | win_rate | action |
|---|---|---|---|---|---|---|
| CAMP-8812 | 5.1 | 4.2 | 21.4 | 0.85 | 68.2 | REDUCE_MODERATE |
| CAMP-4455 | 2.85 | 3.5 | -18.6 | 1.15 | 72.1 | INCREASE_MODERATE |
| CAMP-2190 | 6.35 | 4.8 | 32.3 | 0.7 | 55.4 | REDUCE_AGGRESSIVE |
Use the power of streaming SQL to transform your ad operations and prove the true value of every marketing dollar.