Approach: compute daily conversion rate = (distinct users who reached funnel final step on day) / (distinct users who triggered funnel start on day). Compare today's conversion to the 7-day rolling average (previous 7 full days), flag when relative change > 20% (up or down).Assumptions:- funnel_events is an ordered list like ['step_start','step_2',...,'step_finish'] and we use first and last elements as start_event and end_event.- occurred_at is timestamp; we aggregate by date (CAST to DATE).Query (ANSI SQL):sql
WITH params AS (
SELECT 'step_start' AS start_event, 'step_finish' AS end_event -- replace with actual funnel_events[0] and last
),
daily_counts AS (
SELECT
CAST(e.occurred_at AS DATE) AS day,
p.start_event,
p.end_event,
COUNT(DISTINCT CASE WHEN e.event_name = p.start_event THEN e.user_id END) AS start_users,
COUNT(DISTINCT CASE WHEN e.event_name = p.end_event THEN e.user_id END) AS end_users
FROM events e
CROSS JOIN params p
WHERE e.event_name IN (p.start_event, p.end_event)
GROUP BY CAST(e.occurred_at AS DATE), p.start_event, p.end_event
),
rates AS (
SELECT
day,
start_users,
end_users,
CASE WHEN start_users = 0 THEN NULL ELSE end_users::DECIMAL / start_users END AS conversion_rate
FROM daily_counts
),
rolling AS (
SELECT
day,
conversion_rate,
AVG(conversion_rate) OVER (
ORDER BY day
ROWS BETWEEN 8 PRECEDING AND 1 PRECEDING
) AS prior_7d_avg -- average of the 7 days before current (adjust window size to 7)
FROM rates
)
SELECT
day,
conversion_rate,
prior_7d_avg,
CASE
WHEN prior_7d_avg IS NULL OR conversion_rate IS NULL THEN NULL
ELSE (conversion_rate - prior_7d_avg) / NULLIF(prior_7d_avg,0)
END AS relative_change,
CASE
WHEN prior_7d_avg IS NOT NULL
AND conversion_rate IS NOT NULL
AND ABS((conversion_rate - prior_7d_avg) / NULLIF(prior_7d_avg,0)) > 0.20
THEN TRUE ELSE FALSE END AS drift_alert
FROM rolling
ORDER BY day DESC
LIMIT 30;
How to run in a daily alerting pipeline:- Schedule: run nightly (e.g., 01:00) in your orchestrator (Airflow, cron, db scheduler).- Thresholding: use the drift_alert boolean to trigger an alert. Include metadata (start/end counts, prior_7d_avg, relative_change).- Alert channels: send to PagerDuty/Slack/email with link to dashboard and recent raw data.- Robustness: ignore days with low volume (e.g., start_users < N) to avoid noise; require consecutive days (e.g., 2-day drift) or alert only if absolute difference exceeds a minimum.- Observability: store daily outputs in a monitoring table for audits and backfills; include backfill/re-run capability.- Validation: add automated tests for query correctness and synthetic data checks after deployment.