Documentation Index
Fetch the complete documentation index at: https://mintlify.com/questdb/questdb/llms.txt
Use this file to discover all available pages before exploring further.
Window functions perform calculations across sets of rows related to the current row. They are essential for time-series analysis, rankings, and running calculations.
Window Function Syntax
function(args) OVER (
[PARTITION BY partition_expression]
[ORDER BY sort_expression]
[frame_clause]
)
Ranking Functions
ROW_NUMBER
Assign unique sequential numbers:
SELECT
symbol,
price,
timestamp,
row_number() OVER (PARTITION BY symbol ORDER BY timestamp) AS row_num
FROM trades
ORDER BY symbol, timestamp;
Query Result:
symbol price timestamp row_num
AAPL 150.00 2023-12-28T10:00:00.000000Z 1
AAPL 150.50 2023-12-28T10:30:00.000000Z 2
AAPL 151.00 2023-12-28T11:00:00.000000Z 3
GOOG 140.00 2023-12-28T10:15:00.000000Z 1
GOOG 142.00 2023-12-28T11:00:00.000000Z 2
RANK
Rank with gaps for ties:
SELECT
symbol,
price,
rank() OVER (ORDER BY price DESC) AS price_rank
FROM trades;
Query Result:
symbol price price_rank
MSFT 252.00 1
MSFT 250.00 2
AAPL 151.00 3
AAPL 150.50 4
DENSE_RANK
Rank without gaps:
SELECT
symbol,
price,
dense_rank() OVER (ORDER BY price DESC) AS dense_rank
FROM trades;
Aggregate Window Functions
Running Totals
Calculate cumulative sums:
SELECT
symbol,
timestamp,
quantity,
sum(quantity) OVER (
PARTITION BY symbol
ORDER BY timestamp
) AS cumulative_volume
FROM trades
ORDER BY symbol, timestamp;
Query Result:
symbol timestamp quantity cumulative_volume
AAPL 2023-12-28T10:00:00.000000Z 100 100
AAPL 2023-12-28T10:30:00.000000Z 150 250
AAPL 2023-12-28T11:00:00.000000Z 200 450
AAPL 2023-12-28T11:30:00.000000Z 120 570
Moving Averages
Calculate rolling averages:
SELECT
symbol,
timestamp,
price,
avg(price) OVER (
PARTITION BY symbol
ORDER BY timestamp
ROWS BETWEEN 9 PRECEDING AND CURRENT ROW
) AS moving_avg_10
FROM trades
ORDER BY symbol, timestamp;
Query Result:
symbol timestamp price moving_avg_10
AAPL 2023-12-28T10:00:00.000000Z 150.00 150.00
AAPL 2023-12-28T10:30:00.000000Z 150.50 150.25
AAPL 2023-12-28T11:00:00.000000Z 151.00 150.50
AAPL 2023-12-28T11:30:00.000000Z 151.50 150.75
Running Statistics
Calculate cumulative statistics:
SELECT
symbol,
timestamp,
price,
min(price) OVER (PARTITION BY symbol ORDER BY timestamp) AS running_min,
max(price) OVER (PARTITION BY symbol ORDER BY timestamp) AS running_max,
avg(price) OVER (PARTITION BY symbol ORDER BY timestamp) AS running_avg
FROM trades
ORDER BY symbol, timestamp;
Offset Functions
LAG
Access previous row value:
SELECT
symbol,
timestamp,
price,
lag(price) OVER (PARTITION BY symbol ORDER BY timestamp) AS prev_price,
price - lag(price) OVER (PARTITION BY symbol ORDER BY timestamp) AS price_change
FROM trades
ORDER BY symbol, timestamp;
Query Result:
symbol timestamp price prev_price price_change
AAPL 2023-12-28T10:00:00.000000Z 150.00 null null
AAPL 2023-12-28T10:30:00.000000Z 150.50 150.00 0.50
AAPL 2023-12-28T11:00:00.000000Z 151.00 150.50 0.50
AAPL 2023-12-28T11:30:00.000000Z 151.50 151.00 0.50
Specify offset and default value:
SELECT
symbol,
price,
lag(price, 2, 0) OVER (PARTITION BY symbol ORDER BY timestamp) AS price_2_rows_ago
FROM trades;
LEAD
Access next row value:
SELECT
symbol,
timestamp,
price,
lead(price) OVER (PARTITION BY symbol ORDER BY timestamp) AS next_price,
lead(price) OVER (PARTITION BY symbol ORDER BY timestamp) - price AS future_change
FROM trades
ORDER BY symbol, timestamp;
Query Result:
symbol timestamp price next_price future_change
AAPL 2023-12-28T10:00:00.000000Z 150.00 150.50 0.50
AAPL 2023-12-28T10:30:00.000000Z 150.50 151.00 0.50
AAPL 2023-12-28T11:00:00.000000Z 151.00 151.50 0.50
AAPL 2023-12-28T11:30:00.000000Z 151.50 null null
FIRST_VALUE
Get first value in window:
SELECT
symbol,
timestamp,
price,
first_value(price) OVER (
PARTITION BY symbol
ORDER BY timestamp
) AS opening_price
FROM trades
ORDER BY symbol, timestamp;
LAST_VALUE
Get last value in window:
SELECT
symbol,
timestamp,
price,
last_value(price) OVER (
PARTITION BY symbol
ORDER BY timestamp
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS closing_price
FROM trades
ORDER BY symbol, timestamp;
Frame Specifications
ROWS Frame
Define window by row count:
-- Last 5 rows including current
ROWS BETWEEN 4 PRECEDING AND CURRENT ROW
-- Last 5 rows excluding current
ROWS BETWEEN 5 PRECEDING AND 1 PRECEDING
-- Current row and next 5 rows
ROWS BETWEEN CURRENT ROW AND 5 FOLLOWING
-- All rows from start to current
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
-- All rows in partition
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
RANGE Frame
Define window by value range:
-- Rows within timestamp range
RANGE BETWEEN INTERVAL '5' MINUTE PRECEDING AND CURRENT ROW
-- Rows within numeric range
RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING
Time-Series Window Patterns
Price Returns
Calculate percentage returns:
SELECT
symbol,
timestamp,
price,
(price - lag(price) OVER (PARTITION BY symbol ORDER BY timestamp)) /
lag(price) OVER (PARTITION BY symbol ORDER BY timestamp) * 100 AS return_pct
FROM trades
ORDER BY symbol, timestamp;
Query Result:
symbol timestamp price return_pct
AAPL 2023-12-28T10:00:00.000000Z 150.00 null
AAPL 2023-12-28T10:30:00.000000Z 150.50 0.33
AAPL 2023-12-28T11:00:00.000000Z 151.00 0.33
AAPL 2023-12-28T11:30:00.000000Z 151.50 0.33
Moving Standard Deviation
Calculate rolling volatility:
SELECT
symbol,
timestamp,
price,
stddev_samp(price) OVER (
PARTITION BY symbol
ORDER BY timestamp
ROWS BETWEEN 19 PRECEDING AND CURRENT ROW
) AS volatility_20
FROM trades
ORDER BY symbol, timestamp;
Bollinger Bands
Calculate Bollinger Bands:
SELECT
symbol,
timestamp,
price,
avg(price) OVER w AS middle_band,
avg(price) OVER w + 2 * stddev_samp(price) OVER w AS upper_band,
avg(price) OVER w - 2 * stddev_samp(price) OVER w AS lower_band
FROM trades
WINDOW w AS (
PARTITION BY symbol
ORDER BY timestamp
ROWS BETWEEN 19 PRECEDING AND CURRENT ROW
)
ORDER BY symbol, timestamp;
Time Since Last Event
Calculate time elapsed:
SELECT
symbol,
timestamp,
datediff(
's',
lag(timestamp) OVER (PARTITION BY symbol ORDER BY timestamp),
timestamp
) AS seconds_since_last_trade
FROM trades
ORDER BY symbol, timestamp;
Query Result:
symbol timestamp seconds_since_last_trade
AAPL 2023-12-28T10:00:00.000000Z null
AAPL 2023-12-28T10:30:00.000000Z 1800
AAPL 2023-12-28T11:00:00.000000Z 1800
AAPL 2023-12-28T11:30:00.000000Z 1800
Running High/Low
Track running extremes:
SELECT
symbol,
timestamp,
price,
max(price) OVER (
PARTITION BY symbol
ORDER BY timestamp
) AS running_high,
min(price) OVER (
PARTITION BY symbol
ORDER BY timestamp
) AS running_low
FROM trades
ORDER BY symbol, timestamp;
Session Indicators
Identify new sessions:
SELECT
symbol,
timestamp,
CASE
WHEN lag(timestamp) OVER (PARTITION BY symbol ORDER BY timestamp) IS NULL
OR datediff('m', lag(timestamp) OVER (PARTITION BY symbol ORDER BY timestamp), timestamp) > 5
THEN 1
ELSE 0
END AS new_session
FROM trades
ORDER BY symbol, timestamp;
Named Windows
Define reusable window specifications:
SELECT
symbol,
timestamp,
price,
avg(price) OVER w AS moving_avg,
stddev_samp(price) OVER w AS moving_stddev,
min(price) OVER w AS moving_min,
max(price) OVER w AS moving_max
FROM trades
WINDOW w AS (
PARTITION BY symbol
ORDER BY timestamp
ROWS BETWEEN 9 PRECEDING AND CURRENT ROW
)
ORDER BY symbol, timestamp;
Filtering Window Results
Filter based on window calculations:
-- Get latest trade per symbol
SELECT symbol, price, timestamp
FROM (
SELECT
symbol,
price,
timestamp,
row_number() OVER (PARTITION BY symbol ORDER BY timestamp DESC) AS rn
FROM trades
)
WHERE rn = 1;
Query Result:
symbol price timestamp
AAPL 151.50 2023-12-28T11:30:00.000000Z
GOOG 142.50 2023-12-28T11:15:00.000000Z
MSFT 250.75 2023-12-28T11:20:00.000000Z
Partition Efficiency
Partition by indexed columns:
-- Efficient: symbol is indexed
SELECT
symbol,
avg(price) OVER (PARTITION BY symbol ORDER BY timestamp)
FROM trades;
Frame Size
Smaller frames process faster:
-- Fast: small fixed frame
ROWS BETWEEN 9 PRECEDING AND CURRENT ROW
-- Slower: unbounded frame
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
LATEST ON Alternative
For latest values, use LATEST ON instead of window functions:
-- Faster
SELECT symbol, price, timestamp
FROM trades
LATEST ON timestamp PARTITION BY symbol;
-- Slower
SELECT symbol, price, timestamp
FROM (
SELECT
symbol,
price,
timestamp,
row_number() OVER (PARTITION BY symbol ORDER BY timestamp DESC) AS rn
FROM trades
)
WHERE rn = 1;
Examples
Daily VWAP (Volume-Weighted Average Price)
SELECT
symbol,
date_trunc('day', timestamp) AS day,
sum(price * quantity) / sum(quantity) AS vwap
FROM trades
GROUP BY symbol, day
ORDER BY day DESC, symbol;
Exponential Moving Average
Approximate EMA using window functions:
WITH params AS (
SELECT 0.1 AS alpha -- smoothing factor
)
SELECT
symbol,
timestamp,
price,
avg(price) OVER (
PARTITION BY symbol
ORDER BY timestamp
ROWS BETWEEN 19 PRECEDING AND CURRENT ROW
) AS ema_20
FROM trades, params
ORDER BY symbol, timestamp;
Top N Per Group
Get top 3 trades per symbol by quantity:
SELECT symbol, quantity, price, timestamp
FROM (
SELECT
symbol,
quantity,
price,
timestamp,
row_number() OVER (PARTITION BY symbol ORDER BY quantity DESC) AS rank
FROM trades
)
WHERE rank <= 3
ORDER BY symbol, rank;
Next Steps