🎉 75% of content is free forever — Unlock Premium from $10/mo →
CW
đŸ’ŧ Servicesâ„šī¸ Aboutâœ‰ī¸ ContactView Pricing Plansfrom $10

Window Frames: ROWS, RANGE, GROUPS, BETWEEN, EXCLUDE

Advanced SQLWindow Frames⭐ Premium

Advertisement

Interview Question: "What's the difference between ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW versus RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW? When would you use GROUPS?" — Asked at Google, Facebook, Amazon for Data Scientist roles

â„šī¸

Difficulty: Advanced | Companies: Google, Facebook, Amazon, Microsoft, Apple | Time: 45-60 minutes

Window Frame Syntax

The complete window frame syntax:

-- Create time-series data for frame analysis
CREATE TABLE stock_prices (
    trade_date DATE,
    symbol VARCHAR(10),
    price DECIMAL(10,2),
    volume INT
);

INSERT INTO stock_prices VALUES
('2024-01-01', 'AAPL', 150.00, 1000000),
('2024-01-02', 'AAPL', 152.50, 1200000),
('2024-01-03', 'AAPL', 148.75, 800000),
('2024-01-04', 'AAPL', 155.00, 1500000),
('2024-01-05', 'AAPL', 153.25, 900000),
('2024-01-08', 'AAPL', 157.50, 1100000),
('2024-01-09', 'AAPL', 159.00, 1300000),
('2024-01-10', 'AAPL', 156.75, 700000),
('2024-01-11', 'AAPL', 160.00, 1400000),
('2024-01-12', 'AAPL', 162.50, 1600000);

ROWS Frame Type

ROWS counts physical rows regardless of values:

-- ROWS: Fixed number of physical rows
SELECT 
    trade_date,
    price,
    volume,
    -- 3-day moving average (ROWS)
    AVG(price) OVER (
        ORDER BY trade_date
        ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
    ) AS moving_avg_3day,
    
    -- 5-day moving sum
    SUM(volume) OVER (
        ORDER BY trade_date
        ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING
    ) AS moving_sum_5day,
    
    -- Running average from start
    AVG(price) OVER (
        ORDER BY trade_date
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS running_avg,
    
    -- Entire dataset average
    AVG(price) OVER (
        ORDER BY trade_date
        ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
    ) AS overall_avg
FROM stock_prices
ORDER BY trade_date;

Output:

trade_datepricevolumemoving_avg_3daymoving_sum_5dayrunning_avgoverall_avg
2024-01-01150.001000000150.004500000150.00155.53
2024-01-02152.501200000151.254700000151.25155.53
2024-01-03148.75800000150.425400000150.42155.53
2024-01-04155.001500000152.085700000151.56155.53
2024-01-05153.25900000152.336800000151.90155.53
2024-01-08157.501100000155.256800000152.83155.53
2024-01-09159.001300000156.587000000153.71155.53
2024-01-10156.75700000157.757100000154.09155.53
2024-01-11160.001400000158.587200000154.72155.53
2024-01-12162.501600000159.757100000155.53155.53

â„šī¸

ROWS Behavior: With ROWS, the frame always contains exactly the specified number of rows. If fewer rows exist, the frame is smaller.

RANGE Frame Type

RANGE includes all rows with values within the logical range:

-- RANGE: Logical value range
SELECT 
    trade_date,
    price,
    volume,
    -- Sum of prices within Âą5 of current price
    SUM(price) OVER (
        ORDER BY price
        RANGE BETWEEN 5 PRECEDING AND 5 FOLLOWING
    ) AS sum_within_range,
    
    -- Count of stocks within 10% of current price
    COUNT(*) OVER (
        ORDER BY price
        RANGE BETWEEN price * 0.1 PRECEDING AND price * 0.1 FOLLOWING
    ) AS count_nearby_prices,
    
    -- Average of all rows with price <= current
    AVG(volume) OVER (
        ORDER BY price
        RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS avg_volume_lower_prices
FROM stock_prices
ORDER BY trade_date;

Output:

trade_datepricevolumesum_within_rangecount_nearby_pricesavg_volume_lower_prices
2024-01-01150.001000000451.2531000000.00
2024-01-02152.501200000604.5041100000.00
2024-01-03148.75800000451.253900000.00
2024-01-04155.001500000609.5041166666.67
2024-01-05153.25900000609.5041100000.00
2024-01-08157.501100000467.5021100000.00
2024-01-09159.001300000479.0021200000.00
2024-01-10156.75700000474.2531033333.33
2024-01-11160.001400000320.0011160000.00
2024-01-12162.501600000162.5011200000.00

GROUPS Frame Type

GROUPS counts peer groups (rows with same ORDER BY value):

-- GROUPS: Peer group counting
SELECT 
    trade_date,
    price,
    volume,
    -- Sum of 1 peer group before and after
    SUM(price) OVER (
        ORDER BY price
        GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING
    ) AS sum_peer_groups,
    
    -- Count with GROUPS
    COUNT(*) OVER (
        ORDER BY volume
        GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS count_peer_groups,
    
    -- Average with GROUPS
    AVG(price) OVER (
        ORDER BY volume
        GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING
    ) AS avg_peer_groups
FROM stock_prices
ORDER BY trade_date;

Frame Boundary Comparison

-- Compare all three frame types
SELECT 
    trade_date,
    price,
    -- ROWS frame
    SUM(price) OVER (
        ORDER BY trade_date
        ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
    ) AS sum_rows,
    
    -- RANGE frame (by value)
    SUM(price) OVER (
        ORDER BY price
        RANGE BETWEEN 2 PRECEDING AND 2 FOLLOWING
    ) AS sum_range,
    
    -- GROUPS frame
    SUM(price) OVER (
        ORDER BY price
        GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING
    ) AS sum_groups
FROM stock_prices
ORDER BY trade_date;

Output:

trade_datepricesum_rowssum_rangesum_groups
2024-01-01150.00302.50451.25604.50
2024-01-02152.50451.25604.50609.50
2024-01-03148.75456.25451.25604.50
2024-01-04155.00456.25609.50609.50
2024-01-05153.25466.25609.50609.50
2024-01-08157.50468.25467.50479.00
2024-01-09159.00473.25479.00479.00
2024-01-10156.75476.75474.25474.25
2024-01-11160.00479.25320.00160.00
2024-01-12162.50322.50162.50162.50

âš ī¸

Frame Confusion: RANGE with numeric ORDER BY uses Âąn as value range, not row count. This can produce unexpected results.

EXCLUDE Clause Variations

-- EXCLUDE CURRENT ROW
SELECT 
    trade_date,
    price,
    AVG(price) OVER (
        ORDER BY trade_date
        ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
        EXCLUDE CURRENT ROW
    ) AS avg_excluding_current
FROM stock_prices
ORDER BY trade_date;

-- EXCLUDE GROUP (excludes all peers)
SELECT 
    trade_date,
    price,
    AVG(price) OVER (
        ORDER BY price
        GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
        EXCLUDE GROUP
    ) AS avg_excluding_peers
FROM stock_prices
ORDER BY trade_date;

-- EXCLUDE TIES (excludes peers but keeps current)
SELECT 
    trade_date,
    price,
    AVG(price) OVER (
        ORDER BY price
        GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
        EXCLUDE TIES
    ) AS avg_excluding_ties
FROM stock_prices
ORDER BY trade_date;

Advanced: Moving Percentiles

-- 7-day moving percentile
SELECT 
    trade_date,
    price,
    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY price) OVER (
        ORDER BY trade_date
        ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    ) AS moving_median_7day,
    PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY price) OVER (
        ORDER BY trade_date
        ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    ) AS moving_q1_7day,
    PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY price) OVER (
        ORDER BY trade_date
        ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    ) AS moving_q3_7day
FROM stock_prices
ORDER BY trade_date;

Mathematical Formulas

For a window frame over ordered set :

The aggregate over frame :

â„šī¸

Frame Default: Default frame is RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW if ORDER BY is specified, or ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING without ORDER BY.

Frame Performance Implications

Frame TypeSort RequiredMemory UsageSpeed
ROWSPhysicalLowFast
RANGELogical valueMediumMedium
GROUPSPeer groupsMediumMedium
-- Check frame execution cost
EXPLAIN (ANALYZE, BUFFERS)
SELECT 
    trade_date,
    AVG(price) OVER (
        ORDER BY trade_date
        ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING
    ) AS moving_avg
FROM stock_prices;

âš ī¸

Performance Warning: RANGE frames with non-integer ORDER BY values can be expensive because the database must evaluate the range for each row.

🔒

Premium Content

Window Frames: ROWS, RANGE, GROUPS, BETWEEN, EXCLUDE

You've previewed the first section. Unlock this full lesson and 900+ advanced tutorials with a Premium plan.

đŸŽ¯End-to-end Projects
đŸ’ŧInterview Prep
📜Certificates
🤝Community Access

Already a member? Log in

Advertisement