window_funnel
Aggregate function for conversion funnel analysis. Searches for the longest chain of sequential conditions within a time window. Returns the maximum funnel step reached.
Signature
window_funnel(window INTERVAL, timestamp TIMESTAMP,
cond1 BOOLEAN, cond2 BOOLEAN [, ...]) -> INTEGER
window_funnel(window INTERVAL, mode VARCHAR, timestamp TIMESTAMP,
cond1 BOOLEAN, cond2 BOOLEAN [, ...]) -> INTEGER
Parameters:
| Parameter | Type | Description |
|---|---|---|
window | INTERVAL | Maximum time window from the first step |
mode | VARCHAR | Optional comma-separated mode string |
timestamp | TIMESTAMP | Event timestamp |
cond1..condN | BOOLEAN | Funnel step conditions (2 to 32) |
Returns: INTEGER -- the number of matched funnel steps (0 to N). A return
value of 0 means the entry condition was never satisfied.
Usage
Default Mode
SELECT user_id,
window_funnel(INTERVAL '1 hour', event_time,
event_type = 'page_view',
event_type = 'add_to_cart',
event_type = 'checkout',
event_type = 'purchase'
) as furthest_step
FROM events
GROUP BY user_id;
With Mode String
SELECT user_id,
window_funnel(INTERVAL '1 hour', 'strict_increase, strict_once',
event_time,
event_type = 'page_view',
event_type = 'add_to_cart',
event_type = 'purchase'
) as furthest_step
FROM events
GROUP BY user_id;
Behavior
- Events are sorted by timestamp.
- For each event matching the entry condition (
cond1), a forward scan begins. - The scan attempts to match
cond2,cond3, ...,condNin order, subject to the time window constraint: each subsequent event must occur withinwindowof the entry event. - The maximum step reached across all entry points is returned.
A single event can advance multiple funnel steps in default mode. For example,
if an event satisfies both cond2 and cond3, it advances the funnel by two
steps in a single pass.
Example
Given events for a user with a 1-hour window and 3-step funnel:
| event_time | page_view | add_to_cart | purchase |
|---|---|---|---|
| 10:00 | true | false | false |
| 10:20 | false | true | false |
| 11:30 | false | false | true |
Result: 2
- Step 1 matched at 10:00 (page_view).
- Step 2 matched at 10:20 (add_to_cart, within 1 hour of 10:00).
- Step 3 at 11:30 is outside the 1-hour window from the entry at 10:00.
Determinism
Events sort by (timestamp, conditions) before the scan, so results are
deterministic regardless of thread count, physical row order, or the order in
which DuckDB's parallel aggregation combines partial states — including
same-timestamp event bursts.
Modes
Modes are independently combinable via a comma-separated string parameter. Each mode adds an additional constraint on top of the default greedy scan.
| Mode | Description |
|---|---|
strict | If the previously-matched condition fires again, the chain breaks. Prevents backwards movement. ClickHouse: 'strict' or 'strict_deduplication'. |
strict_deduplication | Alias for strict (matches ClickHouse, where both strings map to the same behavior). |
strict_order | Events must satisfy conditions in exact sequential order. No events matching earlier conditions are allowed between matched steps. |
strict_increase | Requires strictly increasing timestamps between consecutive matched steps. Same-timestamp events cannot advance the funnel. |
strict_once | Each event can advance the funnel by at most one step, even if it satisfies multiple consecutive conditions. |
allow_reentry | If the entry condition fires again after step 1, the funnel resets from that new entry point. |
timestamp_dedup | Extension mode. Events with the same timestamp as the previously matched step are skipped. Not present in ClickHouse. |
Mode Combinations
Modes can be combined freely:
-- Require strictly increasing timestamps and one step per event
window_funnel(INTERVAL '1 hour', 'strict_increase, strict_once',
ts, cond1, cond2, cond3)
-- Strict order with reentry
window_funnel(INTERVAL '1 hour', 'strict_order, allow_reentry',
ts, cond1, cond2, cond3)
Errors
Invalid configuration aborts the query with a descriptive SQL error instead of silently producing wrong results:
- Unknown mode string —
window_funnel: unknown mode 'strict_typo'; valid modes are 'strict', 'strict_deduplication', 'strict_order', 'strict_increase', 'strict_once', 'allow_reentry', 'timestamp_dedup' (comma-separated for combinations) - Month-based window — month intervals are ambiguous (28-31 days); use
day/hour/minute/second units (e.g.
INTERVAL '30 days') - Negative window — the window must be non-negative
A NULL window or mode is skipped leniently (the row contributes no
configuration), matching SQL aggregate conventions.
Implementation
Events are collected during the update phase and sorted by timestamp during finalize. A greedy forward scan from each entry point finds the longest chain.
| Operation | Complexity |
|---|---|
| Update | O(1) amortized (event append) |
| Combine | O(m) where m = events in other state |
| Finalize | O(n * k) where n = events, k = conditions |
| Space | O(n) -- all collected events |
At benchmark scale, window_funnel processes 100 million events in 791 ms
(126 Melem/s).
See Also
sequence_match-- NFA-based pattern matching for more flexible event sequencessequence_count-- count non-overlapping pattern occurrencessequence_next_node-- find what happens after a matched pattern- ClickHouse Compatibility -- full compatibility matrix including all mode mappings