Named Windows — The WINDOW Clause

When a query uses the same window definition multiple times, repeating the full OVER (PARTITION BY ... ORDER BY ... ROWS BETWEEN ...) clause for every function is verbose and error-prone. The WINDOW clause lets you name a window once and reference it across multiple functions.


The problem: repetition

-- Without named windows — same definition repeated 4 times
WITH product_revenue AS (
    SELECT
        p.product_id,
        p.name,
        p.category,
        COALESCE(SUM(oi.quantity * oi.unit_price), 0) AS revenue
    FROM products p
    LEFT JOIN order_items oi ON oi.product_id = p.product_id
    GROUP BY p.product_id, p.name, p.category
)
SELECT
    name,
    category,
    revenue,
    RANK()       OVER (PARTITION BY category ORDER BY revenue DESC) AS cat_rank,
    DENSE_RANK() OVER (PARTITION BY category ORDER BY revenue DESC) AS cat_dense_rank,
    PERCENT_RANK() OVER (PARTITION BY category ORDER BY revenue DESC) AS cat_pct_rank,
    FIRST_VALUE(name) OVER (PARTITION BY category ORDER BY revenue DESC
                            ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS cat_leader
FROM product_revenue;

Four identical (or near-identical) window definitions. If you need to change the partition or sort, you change it in four places — a maintenance hazard.


The WINDOW clause

Define windows once at the bottom of the query, then reference them by name in OVER:

WITH product_revenue AS (
    SELECT
        p.product_id,
        p.name,
        p.category,
        COALESCE(SUM(oi.quantity * oi.unit_price), 0) AS revenue
    FROM products p
    LEFT JOIN order_items oi ON oi.product_id = p.product_id
    GROUP BY p.product_id, p.name, p.category
)
SELECT
    name,
    category,
    revenue,
    RANK()         OVER cat_window                               AS cat_rank,
    DENSE_RANK()   OVER cat_window                               AS cat_dense_rank,
    ROUND((PERCENT_RANK() OVER cat_window)::NUMERIC, 3)                     AS cat_pct_rank,
    FIRST_VALUE(name) OVER (cat_window
        ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS cat_leader
FROM product_revenue
WINDOW cat_window AS (PARTITION BY category ORDER BY revenue DESC)
ORDER BY category, revenue DESC;

The WINDOW clause lives between HAVING and ORDER BY. You can define multiple named windows:

WINDOW
    cat_window    AS (PARTITION BY category ORDER BY revenue DESC),
    global_window AS (ORDER BY revenue DESC)

Extending a named window

You can reference a named window inside OVER() and add more clauses — but you cannot override clauses already defined in the named window:

WINDOW base_w AS (PARTITION BY category ORDER BY revenue DESC)

-- Valid: add a frame clause to the named window
OVER (base_w ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)

-- Invalid: cannot re-specify ORDER BY if already in named window
OVER (base_w ORDER BY revenue ASC)   -- ERROR

This lets you define a base partition + sort once, then customise the frame per function.

Purchase this course to unlock the full lesson.

Sign up