ROWS BETWEEN and RANGE BETWEEN both define the window frame for window functions, but they differ in how they interpret "distance" from the current row.- ROWS BETWEEN: physical offset by row count. "ROWS BETWEEN 2 PRECEDING AND CURRENT ROW" looks at exactly that many rows relative to the current row, regardless of values.- RANGE BETWEEN: logical offset by value of ORDER BY expression. "RANGE BETWEEN INTERVAL '1 day' PRECEDING AND CURRENT ROW" includes all rows whose ORDER BY value falls within that value range. If ORDER BY has duplicates, RANGE can include multiple rows with identical ORDER BY values even if they are outside a fixed row count.Examples with duplicate timestamps (PostgreSQL):sql
CREATE TEMP TABLE t(ts timestamptz, val int);
INSERT INTO t VALUES
('2025-01-01 00:00',1),
('2025-01-01 00:00',2),
('2025-01-01 01:00',3),
('2025-01-01 02:00',4);
-- ROWS: last 2 rows including current (physical)
SELECT ts,val,
sum(val) OVER (ORDER BY ts ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS sum_rows
FROM t
ORDER BY ts, val;
-- RANGE: last 1-hour window by timestamp value
SELECT ts,val,
sum(val) OVER (ORDER BY ts RANGE BETWEEN '1 hour'::interval PRECEDING AND CURRENT ROW) AS sum_range
FROM t
ORDER BY ts, val;
Behavioral difference:- For the two rows with identical ts='2025-01-01 00:00', ROWS with "1 PRECEDING" computes per physical neighbor; the first row sees only itself, the second sees the first + second. RANGE with 1-hour preceding considers both identical timestamps simultaneously — both rows will include each other because their ORDER BY values are equal, so they see the same aggregated set.Performance and semantics:- ROWS is deterministic and simple: planner can use row-position based operations, often streaming-friendly and predictable for pagination.- RANGE can be heavier: it requires comparing ORDER BY values and may translate into more complex plans (e.g., grouping equal values), especially with wide frames or non-sargable expressions; some databases (including older PG versions) have limited RANGE support (only numeric/date with simple offsets).- RANGE can produce variable-size frames (many duplicates → large frame) which can increase memory and CPU for aggregations.When to use which:- Use ROWS when you need a fixed number of preceding/following rows (e.g., rolling N rows), deterministic performance, or order ties matter by physical neighbor.- Use RANGE when you need logical windows based on value distance (time ranges, price ranges) so results align with value intervals; ensure ORDER BY expression and duplicates are understood and that the DB supports efficient RANGE semantics.Best practice: pick ROWS for stable performance and use RANGE only when semantic correctness requires value-based windows; test with realistic duplicate distributions and inspect query plans.