liveBook · Manning

Common table expression

As explained in Just Use Postgres!

A common table expression is a named temporary query result defined within a statement and available to a subsequent primary query in that statement.

A common table expression (CTE) names the result of an auxiliary SQL statement so a subsequent primary statement can use it. Defined with WITH, it breaks complex queries into smaller, readable pieces and behaves like a temporary table or view limited to that statement. section 3.3 section 3.6

Use CTEs to:

  • Filter and prepare data before aggregation, ranking, or other processing. section 3.3.1
  • Chain several transformations, allowing each later CTE to reference earlier ones. section 3.3.2
  • Modify data with INSERT, UPDATE, DELETE, or MERGE and pass returned rows to the primary statement. section 3.3 section 3.3.3

Rank fully played songs

WITH plays_cte AS ( SELECT s.title, s.duration FROM streaming.plays p JOIN streaming.songs s ON p.song_id = s.id WHERE p.play_start_time::DATE BETWEEN '2024-09-15' AND '2024-09-16' AND p.play_duration = s.duration ) SELECT title, COUNT(*) AS play_count FROM plays_cte GROUP BY title ORDER BY play_count DESC; The result ranks Song D first with four plays, followed by Song A with three and Song G with two. Replace the CTE filter and primary aggregation to produce a least-popular ranking. section 3.3.1

Chain CTEs for a business rule

One CTE can collect songs played for less than half their duration; a second can count distinct users and total plays; the primary query can retain only songs heard by at least three users. This separates filtering, aggregation, and selection into readable stages. section 3.3.2

Modify and inspect data in one statement

WITH updated_play AS ( UPDATE streaming.plays SET play_duration = 200 WHERE id = 30 RETURNING song_id, play_duration ) SELECT s.title, s.duration, CASE WHEN up.play_duration = s.duration THEN 'Moved Up the Rank' ELSE 'Rank Not Changed' END AS rank_change_status FROM updated_play up JOIN streaming.songs s ON s.id = up.song_id; RETURNING supplies the changed row to the primary query, which reports whether the song reached its full duration. section 3.3.3

Treat CTEs primarily as a clarity tool, not a guaranteed execution boundary. PostgreSQL may fold a SELECT CTE into the surrounding query or transform it; compare plans with EXPLAIN ANALYZE when performance matters. Data-modifying CTEs execute even when unreferenced, and sibling CTEs run concurrently, so their changes are not visible to one another unless a later CTE reads the modifying CTE's RETURNING output. section 3.3.1 section 3.3.3

WITH cte_name AS (
    auxiliary_statement
)
primary_statement	

Use the compact WITH template to identify the auxiliary statement, its name, and the primary statement that consumes its result. section 3.3

How Just Use Postgres! covers it

How deep it goes
Explains syntax, chained queries, data modification, execution plans, and visibility, while deferring broader EXPLAIN details.
Examples
Demonstrates music-play filtering, popularity rankings, chained user counts, updates with RETURNING, and vector similarity search.
What it assumes
Assumes familiarity with regular SQL queries, joins, filtering, grouping, and aggregation while introducing modern SQL capabilities.
What you can do after
Apply it via PostgreSQL SQL to build readable multi-stage queries and inspect their actual execution with EXPLAIN ANALYZE.
How it connects
Connects WITH clauses to SELECT, INSERT, UPDATE, DELETE, MERGE, subqueries, aggregation, ranking, and EXPLAIN.

Also explained in

Compare how 6 books explain common table expression →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Just Use Postgres!. The section links point to the parts of the book it draws on.