CTEs and Window Functions
Name subqueries and compute row rankings with windows. Results appear in the same fence: same-line -- comments when short, multiline -- blocks below the sample when not.
Busque em todas as páginas da documentação
Name subqueries and compute row rankings with windows. Results appear in the same fence: same-line -- comments when short, multiline -- blocks below the sample when not.
Name a subquery for readability and reuse.
WITH paid AS (
SELECT * FROM orders WHERE status = 'paid'
)
SELECT user_id, count(*) FROM paid GROUP BY user_id;
-- counts paid ordersWalk trees/graphs with UNION ALL.
WITH RECURSIVE walk AS (
SELECT id, parent_id, 1 AS depth FROM nodes WHERE id = 1
UNION ALL
SELECT n.id, n.parent_id, w.depth + 1
FROM nodes n
JOIN walk w ON n.parent_id = w.id
)
SELECT * FROM walk;
-- subtree under id=1Unique rank per partition order.
SELECT id, user_id,
row_number() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
FROM events;
-- rn=1 is latest per userReset window per group key.
SELECT dept, name, salary,
avg(salary) OVER (PARTITION BY dept) AS dept_avg
FROM employees;
-- dept_avg constant within deptAccess previous/next row values.
SELECT day, revenue,
lag(revenue) OVER (ORDER BY day) AS prev
FROM daily;
-- prev: prior day revenueRunning total with window frame.
SELECT day,
sum(n) OVER (ORDER BY day ROWS UNBOUNDED PRECEDING) AS running
FROM stats;
-- cumulative nRanks with and without gaps.
SELECT score,
rank() OVER (ORDER BY score DESC) AS r,
dense_rank() OVER (ORDER BY score DESC) AS dr
FROM scores;Split into N roughly equal buckets.
SELECT id, ntile(4) OVER (ORDER BY score) AS quartile
FROM players;
-- quartile 1..4Some aggregates accept FILTER; windows use CASE often.
SELECT count(*) FILTER (WHERE ok) OVER () AS ok_count FROM t;MATERIALIZED / NOT MATERIALIZED hints (PG 12+).
WITH c AS MATERIALIZED (
SELECT * FROM big WHERE x > 1
)
SELECT count(*) FROM c;
-- force materializeWritable CTEs for multi-step DML.
WITH d AS (
DELETE FROM sessions WHERE expired RETURNING user_id
)
SELECT user_id, count(*) FROM d GROUP BY 1;
-- expired session countsPick first in window frame.
SELECT id,
first_value(price) OVER (PARTITION BY sku ORDER BY ts) AS first_price
FROM prices;Frame exclusions (PG 11+).
SELECT sum(n) OVER (
ORDER BY i
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
EXCLUDE CURRENT ROW
) FROM t;Name a window for reuse.
SELECT id,
avg(v) OVER w AS avg3
FROM t
WINDOW w AS (ORDER BY id ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING);row_number filter pattern.
SELECT * FROM (
SELECT *, row_number() OVER (PARTITION BY user_id ORDER BY score DESC) rn
FROM scores
) s WHERE rn <= 3;
-- top 3 scores per userStack versions: PostgreSQL 18.4 (stable 18, maintenance 17) · pgvector 0.8+
Revisado por Chris St. John·Última atualização: 18 de jul. de 2026