Files
vps_web/docs/forum-post-detail-funnel-sql.md
ddrwode d454699f50 哈哈
2026-02-11 17:38:06 +08:00

5.6 KiB

Forum Post Detail Funnel SQL

This project now stores tracking events in table forum_track_events. Daily rollup is also available in forum_track_daily_summary.

Key columns:

  • event_name
  • label
  • post_id
  • user_id
  • visitor_id
  • cta_variant
  • created_at

Use the queries below as baseline funnel cuts.

1) Daily impression -> pricing CTA conversion (MySQL)

SELECT
  DATE(created_at) AS d,
  COALESCE(cta_variant, 'unknown') AS cta_variant,
  COUNT(CASE WHEN event_name = 'post_detail_cta_impression' THEN 1 END) AS impressions,
  COUNT(CASE WHEN event_name = 'post_detail_cta_pricing' THEN 1 END) AS pricing_clicks,
  ROUND(
    COUNT(CASE WHEN event_name = 'post_detail_cta_pricing' THEN 1 END) * 100.0
    / NULLIF(COUNT(CASE WHEN event_name = 'post_detail_cta_impression' THEN 1 END), 0),
    2
  ) AS ctr_pct
FROM forum_track_events
WHERE created_at >= NOW() - INTERVAL 30 DAY
GROUP BY DATE(created_at), COALESCE(cta_variant, 'unknown')
ORDER BY d DESC, cta_variant;

2) Unique visitor funnel by variant (MySQL)

WITH pv AS (
  SELECT
    COALESCE(NULLIF(visitor_id, ''), CONCAT('u:', user_id)) AS actor_id,
    COALESCE(cta_variant, 'unknown') AS cta_variant,
    MAX(event_name = 'post_detail_cta_impression') AS seen_impression,
    MAX(event_name = 'post_detail_cta_pricing') AS clicked_pricing,
    MAX(event_name = 'post_detail_comment_submit') AS submitted_comment
  FROM forum_track_events
  WHERE created_at >= NOW() - INTERVAL 30 DAY
  GROUP BY COALESCE(NULLIF(visitor_id, ''), CONCAT('u:', user_id)), COALESCE(cta_variant, 'unknown')
)
SELECT
  cta_variant,
  COUNT(*) AS actors,
  SUM(seen_impression) AS actors_with_impression,
  SUM(clicked_pricing) AS actors_with_pricing_click,
  SUM(submitted_comment) AS actors_with_comment,
  ROUND(SUM(clicked_pricing) * 100.0 / NULLIF(SUM(seen_impression), 0), 2) AS actor_ctr_pct,
  ROUND(SUM(submitted_comment) * 100.0 / NULLIF(SUM(seen_impression), 0), 2) AS actor_comment_rate_pct
FROM pv
GROUP BY cta_variant
ORDER BY cta_variant;

3) Top performing CTA labels (MySQL)

SELECT
  label,
  COUNT(*) AS clicks
FROM forum_track_events
WHERE event_name IN ('post_detail_cta_pricing', 'post_detail_cta_new_topic', 'post_detail_sidebar_compare')
  AND created_at >= NOW() - INTERVAL 30 DAY
GROUP BY label
ORDER BY clicks DESC
LIMIT 20;

4) Daily impression -> pricing CTA conversion (SQLite)

SELECT
  DATE(created_at) AS d,
  COALESCE(cta_variant, 'unknown') AS cta_variant,
  SUM(CASE WHEN event_name = 'post_detail_cta_impression' THEN 1 ELSE 0 END) AS impressions,
  SUM(CASE WHEN event_name = 'post_detail_cta_pricing' THEN 1 ELSE 0 END) AS pricing_clicks,
  ROUND(
    SUM(CASE WHEN event_name = 'post_detail_cta_pricing' THEN 1 ELSE 0 END) * 100.0
    / NULLIF(SUM(CASE WHEN event_name = 'post_detail_cta_impression' THEN 1 ELSE 0 END), 0),
    2
  ) AS ctr_pct
FROM forum_track_events
WHERE created_at >= DATETIME('now', '-30 days')
GROUP BY DATE(created_at), COALESCE(cta_variant, 'unknown')
ORDER BY d DESC, cta_variant;

5) Post-level conversion ranking (SQLite/MySQL compatible style)

SELECT
  post_id,
  COALESCE(cta_variant, 'unknown') AS cta_variant,
  SUM(CASE WHEN event_name = 'post_detail_cta_impression' THEN 1 ELSE 0 END) AS impressions,
  SUM(CASE WHEN event_name = 'post_detail_cta_pricing' THEN 1 ELSE 0 END) AS pricing_clicks,
  ROUND(
    SUM(CASE WHEN event_name = 'post_detail_cta_pricing' THEN 1 ELSE 0 END) * 100.0
    / NULLIF(SUM(CASE WHEN event_name = 'post_detail_cta_impression' THEN 1 ELSE 0 END), 0),
    2
  ) AS ctr_pct
FROM forum_track_events
WHERE post_id IS NOT NULL
GROUP BY post_id, COALESCE(cta_variant, 'unknown')
HAVING impressions >= 20
ORDER BY ctr_pct DESC, impressions DESC
LIMIT 50;

6) Daily rollup table quick check (SQLite/MySQL compatible style)

SELECT
  event_day,
  cta_variant,
  event_name,
  total
FROM forum_track_daily_summary
WHERE event_day >= DATE('now', '-30 day') -- MySQL: CURDATE() - INTERVAL 30 DAY
ORDER BY event_day DESC, cta_variant, event_name;

7) Mobile sticky-bar exposure rate (SQLite/MySQL compatible style)

SELECT
  DATE(created_at) AS d,
  COALESCE(cta_variant, 'unknown') AS cta_variant,
  SUM(CASE WHEN event_name = 'post_detail_cta_impression' THEN 1 ELSE 0 END) AS impressions,
  SUM(CASE WHEN event_name = 'post_detail_mobile_bar_impression' THEN 1 ELSE 0 END) AS mobile_bar_impressions,
  ROUND(
    SUM(CASE WHEN event_name = 'post_detail_mobile_bar_impression' THEN 1 ELSE 0 END) * 100.0
    / NULLIF(SUM(CASE WHEN event_name = 'post_detail_cta_impression' THEN 1 ELSE 0 END), 0),
    2
  ) AS mobile_bar_exposure_pct
FROM forum_track_events
WHERE created_at >= DATETIME('now', '-30 days')
GROUP BY DATE(created_at), COALESCE(cta_variant, 'unknown')
ORDER BY d DESC, cta_variant;

8) Device split performance (SQLite/MySQL compatible style)

SELECT
  DATE(created_at) AS d,
  COALESCE(device_type, 'unknown') AS device_type,
  COALESCE(cta_variant, 'unknown') AS cta_variant,
  SUM(CASE WHEN event_name = 'post_detail_cta_impression' THEN 1 ELSE 0 END) AS impressions,
  SUM(CASE WHEN event_name = 'post_detail_mobile_bar_impression' THEN 1 ELSE 0 END) AS mobile_bar_impressions,
  SUM(CASE WHEN event_name = 'post_detail_cta_pricing' THEN 1 ELSE 0 END) AS pricing_clicks,
  SUM(CASE WHEN event_name = 'post_detail_cta_pricing' AND label LIKE 'mobile_%' THEN 1 ELSE 0 END) AS mobile_pricing_clicks
FROM forum_track_events
WHERE created_at >= DATETIME('now', '-30 days')
GROUP BY DATE(created_at), COALESCE(device_type, 'unknown'), COALESCE(cta_variant, 'unknown')
ORDER BY d DESC, device_type, cta_variant;