Function Reference

WITH

SQL construct

Defines a named subquery (a common table expression, CTE) that the main query can use like a table.

PostgreSQL 17.5
WITH [RECURSIVE] name_cte [(column[, ...])] AS (subquery) main_query

Parameters

RECURSIVEkeywordoptional
Allows the table expression to refer to itself
name_ctename
Name of the table expression
columnnameoptional
Column names of the table expression, taken from subquery by default
subquerysubquery
Subquery whose result is available by the name name_cte
main_querysubquery
Main query that refers to name_cte like a table

Return value

A temporary named result set available only within this query

Examples

Statuses with more than one member

PostgreSQL 17.5
WITH status_counts AS (
	SELECT status,
		COUNT(*) AS members_count
	FROM FamilyMembers
	GROUP BY status
)
SELECT status,
	members_count
FROM status_counts
WHERE members_count > 1
ORDER BY status;
statusmembers_count
daughter3
father2
mother2

Numbers 1 to 5 with RECURSIVE

PostgreSQL 17.5
WITH RECURSIVE numbers (n) AS (
	SELECT 1
	UNION ALL
	SELECT n + 1
	FROM numbers
	WHERE n < 5
)
SELECT n
FROM numbers;
n
1
2
3
4
5

Subordinates of managers by level

PostgreSQL 17.5
WITH RECURSIVE subordinates AS (
	SELECT id,
		name,
		manager_id,
		1 AS LEVEL
	FROM employees
	WHERE manager_id IS NULL
	UNION ALL
	SELECT e.id,
		e.name,
		e.manager_id,
		s.level + 1
	FROM employees AS e
		JOIN subordinates AS s ON e.manager_id = s.id
)
SELECT id,
	name,
	LEVEL
FROM subordinates
ORDER BY LEVEL,
	id;
idnamelevel
1Anna Ivanova1
6Olga Novikova1
10Tatiana Lebedeva1
13Roman Fedorov1
17Alexey Belov1
20Sergey Kuznetsov1
24Larisa Titova1
28Polina Nikitina1
32Vadim Sorokin1
36Boris Panov1

Details

One WITH can define several expressions separated by commas, and each one can refer to the previous ones.

A recursive expression (WITH RECURSIVE) has two parts joined with UNION ALL or UNION. The initial query runs once and gives the first rows. The recursive part refers to the expression itself and runs again and again: at each step it sees only the rows added at the previous step. The work stops when a step adds no rows.

There is no limit on the number of steps, so the recursive part needs a stop condition, for example WHERE n < 5. UNION drops duplicate rows, which can also stop the recursion, while UNION ALL keeps them all.

See also

Where to learn