Window Frames (ROWS vs RANGE): Concept
Module: Window Functions
Think of ROWS and RANGE as two different ways to count items in a line. ROWS is like counting people: "Give me the 3 people in front of me" - you get exactly 3 individuals, regardless of their characteristics. RANGE is like counting by groups: "Give me everyone with the same ticket type as the 3 groups in front of me" - you might get 3 people or 10 people, depending on group sizes. In SQL terms, ROWS counts physical database rows ("exactly 5 rows before this one"), while RANGE groups by ORDER BY values ("all rows with ORDER BY values in this range"). The choice affects both correctness and performance: ROWS is predictable and fast, RANGE handles ties logically but requires more processing.
**
**Understanding the Fundamental Difference:**
ROWS and RANGE represent two completely different approaches to defining window frames:
**ROWS - Physical Row Counting:**
- Counts actual database rows in their physical order
- Ignores the values in ORDER BY columns
- Always gives predictable, exact counts
- Faster performance due to simple row counting
- Best for sequential analysis and moving averages
**RANGE - Logical Value Grouping:**
- Groups rows by ORDER BY column values
- Includes all rows with "tied" (identical) ORDER BY values
- Can include more or fewer rows than specified
- Slower performance due to value comparison logic
- Best for time-based analysis and tie-inclusive calculations
**Demonstrating the Difference with Ties:**
```sql
-- Sample data with duplicate dates (ties)
WITH sales_data AS (
SELECT * FROM VALUES
('2024-01-01'::date, 'Store A', 100),
('2024-01-01'::date, 'Store B', 150), -- Same date (tie)
('2024-01-02'::date, 'Store A', 200),
('2024-01-03'::date, 'Store A', 120),
('2024-01-03'::date, 'Store B', 180), -- Same date (tie)
('2024-01-04'::date, 'Store A', 140)
AS t(sale_date, store, amount)
)
SELECT
sale_date,
store,
amount,
-- ROWS: Counts exactly 2 physical rows before current
STRING_AGG(amount::text, ', ') OVER (
ORDER BY sale_date, store
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
) as rows_frame_values,
SUM(amount) OVER (
ORDER BY sale_date, store