User Behavior Funnel Analysis: Tracking Conversions from Impression to Order
Business Background
After a promotion goes live, the operations team's biggest concern is usually not "how much did we sell" but "where are we losing users." 100 users saw a product, but only 3 placed an order — was the click-through rate too low, did users abandon after adding to cart, or was there a problem on the checkout page?
Funnel analysis breaks user behavior into an ordered sequence of steps (impression → click → add-to-cart → order), counts the number of users and conversion rate at each step, and pinpoints the stage with the highest drop-off.
Typical use cases:
- Post-promotion review: After a campaign ends, compare funnel differences across products to identify underperforming SKUs
- Page optimization decisions: High click-through but low add-to-cart rate suggests the product detail page lacks persuasiveness; high add-to-cart but low order rate suggests friction in the checkout flow
- Real-time monitoring: During an active campaign, refresh funnel data every minute and intervene immediately when anomalies are detected
Use Cases
| Scenario | Description |
|---|---|
| E-commerce promotion funnel | Impression → click → add-to-cart → order; identify the step with the highest drop-off |
| Registration conversion funnel | Visit landing page → fill form → verify phone → complete registration |
| Content consumption funnel | Push impression → open → finish reading → share |
| A/B test comparison | Compare conversion differences between test and control groups at each step |
SQL Commands Involved
| Command / Function | Purpose |
|---|---|
COUNT(DISTINCT user_id) | Count deduplicated users (UV) per step |
LAG() | Retrieve the UV of the previous step to calculate step-over-step conversion rate |
FIRST_VALUE() | Retrieve the UV of the first funnel step to calculate overall conversion rate |
PARTITION BY | Group by dimensions such as product or time period to compute independent funnels |
CREATE DYNAMIC TABLE | Wrap the funnel query into an auto-refreshing real-time dashboard |
Data Architecture
How event data is written in real time
This guide uses INSERT INTO to simulate user behavior events so you can quickly reproduce the examples in a test environment. In production, user behavior data typically comes from client-side tracking or server-side logs. Singdata Lakehouse provides multiple ways to continuously ingest this data:
| Data source | Recommended method | Description | Reference |
|---|---|---|---|
| Kafka message queue (real-time tracking events) | Pipe continuous ingestion | Suitable when a client SDK sends events to Kafka; millisecond-level latency | Continuous ingestion with the read_kafka function |
| Object storage (log files on disk) | Pipe continuous ingestion | Suitable when server-side logs are periodically written to OSS / COS / S3 | Continuous ingestion from object storage with Pipe |
| Business databases (orders, click records) | Studio real-time sync task (CDC) | Captures source database binlog and syncs to Lakehouse with millisecond-level latency | Real-time sync task |
After new events are written to the base table, the Dynamic Table automatically detects them and performs an incremental computation in the next refresh cycle — no manual trigger needed.
Prerequisites
Create the User Behavior Events Table
event_type takes the values expose (impression), click, cart (add-to-cart), and order, representing the four funnel steps.
Insert Test Event Data
Scenario 1: Overall Funnel Conversion Rate
Count the UV at each step across all users, and calculate the step-over-step conversion rate (each step relative to the previous) and the overall conversion rate (each step relative to impressions).
Result interpretation:
- Impression → click: 75% of users clicked on the product — a normal click-through rate
- Click → add-to-cart: Only 50% of users added to cart, making this the step with the highest drop-off — the product detail page may lack persuasiveness
- Add-to-cart → order: 100% of users who added to cart completed the order — no friction in the checkout flow
- Overall conversion rate: 3 out of 8 users who saw the impression placed an order, for an overall conversion rate of 37.5%
LAG(uv) OVER (ORDER BY step) retrieves the UV from the previous step. The first step (expose) has no previous step, so prev_uv and step_cvr are NULL — this is expected.
Scenario 2: Per-Product Funnel Comparison
Identify conversion differences across products to locate underperforming SKUs.
Result interpretation:
- P001: Click-through rate 75%, but only 25% overall conversion from add-to-cart to order — significant drop-off after adding to cart
- P002: Click-through rate 100%, and no add-to-cart step (users went directly from click to order) — this is normal and indicates a shorter purchase decision path for this product, or that the add-to-cart event was not covered by tracking
- P003: Click-through rate of only 50%, the lowest among the three products — the product detail page needs to be more compelling
PARTITION BY product_id lets each product independently compute FIRST_VALUE (its own impression UV), so conversion rates do not interfere with each other.
Scenario 3: Building a Real-Time Funnel Dashboard with Dynamic Table
Wrap the overall funnel query into a Dynamic Table that auto-refreshes every minute, so the operations dashboard can query the result table directly.
Wait about 1 minute, then query:
After new events are written, the Dynamic Table automatically updates in the next refresh cycle. Simulate a new batch of user behavior:
Wait about 1 minute — the funnel updates automatically:
Impression UV increased from 8 to 10, click-through rate rose from 75% to 80%, and overall conversion rate improved from 37.5% to 40% — the new batch of users converted at a higher rate.
Scenario 4: Comparing Funnels by Time Period
Compare conversion differences between morning and afternoon to assess user quality across time slots.
Result interpretation: The afternoon time slot has a higher click-through rate (100%) and overall conversion rate (50%) than the morning (75% / 37.5%), indicating that afternoon users have stronger purchase intent. Consider increasing push volume during afternoon hours.
PARTITION BY time_slot lets each time period independently compute its conversion rate. FIRST_VALUE(uv) OVER (PARTITION BY time_slot ORDER BY step) uses each time period's own impression UV as the denominator.
Clean Up Resources
Key Takeaways
COUNT(DISTINCT user_id)is the foundation of funnel analysis: Count deduplicated users (UV) at each step — multiple actions by the same user at the same step count only onceFIRST_VALUEcomputes overall conversion rate;LAGcomputes step-over-step conversion rate: Used together, they show both per-step drop-off and cumulative conversion from impression to the current stepPARTITION BYsupports multi-dimensional funnels: Group by product, time period, channel, or other dimensions — each group computes its own conversion rate independently- Missing steps are normal: Some users may skip a step (e.g., going directly from click to order). The corresponding row simply won't appear in the results, which does not affect the calculation of other steps
- Dynamic Table enables real-time funnels: Wrap the funnel SQL in a Dynamic Table, and it automatically refreshes incrementally as new events arrive — no scheduled jobs needed
