The WITH
clause allows you to define temporary result sets (Common Table
Expressions or CTEs) that exist only for the duration of a query. CTEs can
simplify complex queries by breaking them down into more manageable parts.
Create a CTE named numbers
that contains a sequence of integers:
WITH numbers AS (
SELECT * FROM generate_series(1, 5)
)
SELECT * FROM numbers;
This produces:
generate_series |
---|
1 |
2 |
3 |
4 |
5 |
Column aliases may be provided in the CTE definition which can then be used later in the query:
WITH c(a, b, c) AS (
SELECT 4, 5, 6
)
SELECT c, b, a FROM c
If column names aren't provided, then the CTE will use the column names generated by the select expressions inside the CTE.
You can define multiple CTEs in a single WITH
clause by separating them with
commas:
WITH
cte1 AS (select_statement1),
cte2 AS (select_statement2)
SELECT * FROM cte1 JOIN cte2 ON ...;
Create two CTEs and join them together:
WITH
even_numbers AS (
SELECT * FROM generate_series(2, 10, 2)
),
odd_numbers AS (
SELECT * FROM generate_series(1, 9, 2)
)
SELECT e.generate_series AS even, o.generate_series AS odd
FROM even_numbers e JOIN odd_numbers o
ON e.generate_series = o.generate_series + 1;
This produces:
even | odd |
---|---|
2 | 1 |
4 | 3 |
6 | 5 |
8 | 7 |
10 | 9 |
CTEs can only reference CTEs defined earlier in the query.
By default, a CTE is evaluated each time it is referenced in the query. You can
use the MATERIALIZED
to materialize the results which will then be reused
instead of executing the CTE multiple times.
For example, the result of the numbers
CTE will be computed only once, even
though we're referencing numbers
more than once in the query.
WITH numbers AS MATERIALIZED (
SELECT * FROM generate_series(1, 100)
)
SELECT COUNT(*)
FROM numbers n1, numbers n2, numbers n3
This can be useful for CTEs which are expensive to compute, or CTEs which may not be deterministic.
For example, if we have a CTE that calls random
, we can ensure that CTE is
materialize meaning we'll get the same results no matter how many times the CTE
is referenced:
WITH c AS MATERIALIZED (
SELECT random() * 100 AS a
)
SELECT c1.a = c2.a FROM c AS c1, c AS c2;
The result of this query will return true
as we're only executing random
once, and reusing the result.