Sign in to save your place.
Window functions, arrays, lambdas, CTEs, and ClickHouse-specific SQL magic that makes complex analytics a breeze.
Window functions let you compute values across rows without collapsing them (unlike GROUP BY).
-- Running total of revenue by date SELECT event_date, revenue, sum(revenue) OVER (ORDER BY event_date) AS running_total, row_number() OVER (PARTITION BY country ORDER BY revenue DESC) AS rank_in_country, lag(revenue) OVER (ORDER BY event_date) AS prev_day_revenue, revenue - lag(revenue) OVER (ORDER BY event_date) AS day_over_day_change FROM daily_revenue;
Imagine you're in a race. GROUP BY tells you "the average speed of all runners." A window function tells you "your speed AND how you compare to the runner before you AND your running total distance" — all in one row. You see the big picture without losing the details.
ClickHouse has first-class array support — no need for unnest/lateral joins like in PostgreSQL.
-- Create and manipulate arrays SELECT [1, 2, 3, 4, 5] AS arr, arrayMap(x -> x * 2, [1,2,3]) AS doubled, -- [2, 4, 6] arrayFilter(x -> x > 2, [1,2,3,4]) AS filtered, -- [3, 4] arraySum([10, 20, 30]) AS total, -- 60 arrayJoin(['a', 'b', 'c']) AS exploded; -- 3 rows -- Real use case: User's browsing path analysis SELECT user_id, groupArray(url) AS browsing_path, length(groupArray(url)) AS pages_visited, arrayElement(groupArray(url), 1) AS landing_page, arrayElement(groupArray(url), -1) AS exit_page FROM events GROUP BY user_id;
ClickHouse supports inline lambda functions for array processing — like Python's lambda:
-- Lambda syntax: x -> expression SELECT arrayMap(x -> x * x, [1,2,3,4,5]) AS squares, -- [1, 4, 9, 16, 25] arrayFilter(x -> x % 2 = 0, [1,2,3,4,5,6]) AS evens, -- [2, 4, 6] arrayExists(x -> x > 100, [10,50,200]) AS has_big; -- 1 (true)
WITH daily_stats AS ( SELECT event_date, count() AS events, uniq(user_id) AS users FROM events GROUP BY event_date ), averages AS ( SELECT avg(events) AS avg_events, avg(users) AS avg_users FROM daily_stats ) SELECT d.*, round(d.events / a.avg_events * 100, 1) AS pct_of_avg FROM daily_stats d, averages a ORDER BY d.event_date DESC LIMIT 30;
ClickHouse has built-in URL parsing — no regex needed!
SELECT domain('https://shop.example.com/products?id=123'), -- 'shop.example.com' path('https://shop.example.com/products?id=123'), -- '/products' extractURLParameter('https://shop.example.com/products?id=123&color=red', 'color'), -- 'red' topLevelDomain('https://shop.example.com'); -- 'com'
Most databases require complex regex to parse URLs. ClickHouse does it natively, which is why it's the go-to for web analytics (it was literally built for Yandex.Metrica, a Google Analytics competitor).
-- Count events by type in a single query SELECT event_date, countIf(event_type = 'click') AS clicks, countIf(event_type = 'purchase') AS purchases, sumIf(revenue, event_type = 'purchase') AS total_revenue, round(countIf(event_type = 'purchase') / countIf(event_type = 'click') * 100, 2) AS conversion_rate FROM events GROUP BY event_date ORDER BY event_date;
Instead of writing multiple queries or complex CASE WHEN statements, ClickHouse lets you say "count ONLY the rows where this condition is true." It's like asking a teacher: "How many students got an A?" vs "How many students are there total?" — in the same breath!
Checked against the ClickHouse documentation in October 2026. The JSON type has been production-ready since version 25.3. ClickHouse Cloud rewrites a MergeTree create onto the matching SharedMergeTree engine.
Window functions and array functions are ordinary SQL here. Approximate aggregates such as uniq are a choice when an exact distinct count is too expensive. Say which one you used. Higher-order array functions keep the work in one pass. Do not pull a thousand-row array into a dashboard if a GROUP BY would answer the question.
SELECT user_id, uniq(session_id) AS sessions
FROM events
GROUP BY user_idWhat is uniq?