Skip to main content
A common table expression (CTE), also called a WITH query, provides a shorthand name to a possibly complex before it is used in a larger query context. This improves the readability of SQL code. You can use CTEs in combination with and , , , and data-modifying statements.

Synopsis

with_clause syntax diagram

Parameters

ParameterDescription
table_alias_nameThe name to use to refer to the common table expression from the accompanying query or statement.
nameA name for one of the columns in the newly defined common table expression.
preparable_stmtThe statement or subquery to use as common table expression.
MATERIALIZED/NOT MATERIALIZEDOverride the ’s decision to materialize (i.e., store the results) of the common table expression. By default, the optimizer materializes the common table expression if it affects other objects in the database, or if it is used in the query multiple times.

Overview

The examples on this page use MovR, a fictional vehicle-sharing application, to demonstrate CockroachDB SQL statements. To follow along, run from the command line to start a temporary, in-memory cluster with the movr dataset preloaded.For more information about the MovR example application and dataset, see . A query or statement of the form WITH x AS (y) z creates the temporary table name x for the results of the subquery y, to be reused in the context of z.
For example:
In this example, the WITH clause defines the temporary name r for the subquery over rides, and that name becomes a table name for use in any of the subsequent SELECT clause. This query is equivalent to, but simpler to read than:
It is also possible to define multiple common table expressions simultaneously with a single WITH clause, separated by commas. Later subqueries can refer to earlier subqueries by name. For example, the following query is equivalent to the two preceding examples:
In this example, the second CTE results refers to the first CTE r by name. The final query refers to the CTE results.

Nested WITH clauses

You can use a WITH clause in a subquery and a WITH clause within another WITH clause. For example:
When analyzing that mention a CTE name, CockroachDB will choose the CTE definition that is closest to the table expression. For example:
In this example, the inner subquery SELECT * FROM v will select from table vehicles (closest WITH clause), not from table users.
CockroachDB does not support nested WITH clauses containing data-modifying statements. WITH clauses containing data-modifying statements must be at the top level of the query.

Data-modifying statements

You can use a (INSERT, DELETE, etc.) as a common table expression, as long as the WITH clause containing the data-modifying statement is at the top level of the query. For example:
If the WITH clause containing the data-modifying statement is at a lower level, the statement results in an error:
If a common table expression contains a data-modifying statement (INSERT, DELETE, etc.), the modifications are performed fully even if only part of the results are used, e.g., with LIMIT. See Data writes in subqueries for details.

Reference multiple common table expressions

You can reference multiple CTEs in a single query using a WITH operator. For example:
In this single query, you define two CTEs and then reference them in a table join.

Recursive common table expressions

Recursive common table expressions are common table expressions that contain subqueries that refer to their own output. Recursive CTE definitions take the following form:
To write a recursive CTE:
  1. Add the RECURSIVE keyword directly after the WITH operator in the CTE definition, and before the CTE name.
  2. Define an initial, non-recursive subquery. This subquery defines the initial values of the CTE.
  3. Add the UNION or UNION ALL keyword after the initial subquery. The UNION variant deduplicates rows.
  4. Define a recursive subquery that references its own output. This subquery can also reference the CTE name, unlike the initial subquery.
  5. Write a parent query that evaluates the results of the CTE.
CockroachDB evaluates recursive CTEs as follows:
  1. The initial query is evaluated. Its results are stored to rows in the CTE and copied to a temporary, working table. This working table is updated across iterations of the recursive subquery.
  2. The recursive subquery is evaluated iteratively on the contents of the working table. The results of each iteration replace the contents of the working table. The results are also stored to rows of the CTE. The recursive subquery iterates until no results are returned.
Recursive subqueries must eventually return no results, or the query will run indefinitely.

Example

The following recursive CTE calculates the factorial of the numbers 0 through 9:
The initial subquery (VALUES (0, 1)) initializes the working table with the values 0 for the n column and 1 for the factorial column. The recursive subquery (SELECT n+1, (n+1)*factorial FROM cte WHERE n < 9) evaluates over the initial values of the working table and replaces its contents with the results. It then iterates over the contents of the working table, replacing its contents at each iteration, until n reaches 9, when the evaluates as false. If no WHERE clause were defined in the example, the recursive subquery would always return results and loop indefinitely, resulting in an error:
If you are unsure if your recursive subquery will loop indefinitely, you can limit the results of the CTE with the keyword. For example, if you remove the WHERE clause from the factorial example, you can use LIMIT to limit the results and avoid the integer out of range error:
While adding a limit to prevent infinite recursion works for testing and debugging, Cockroach Labs does not recommend it in production. It is best practice to ensure that recursive subqueries have an explicit end condition.

Loose index scan using a recursive CTE

You can use a recursive CTE to perform a loose index scan, which speeds up certain queries that would otherwise require a full scan. A loose index scan reads noncontiguous ranges of an index by performing multiple shorter scans. In this example, compare the latencies when scanning an index with and without a recursive CTE: Create a table:
Populate the table with many random values from 0 to 9:
Create an index:
Issue a statement to count the number of distinct values, without using a recursive CTE:
This statement has a high latency because it reads every row in the index. You can see this using :
Instead, use a recursive CTE to perform a loose index scan:
The initial subquery uses the and clauses to select the lowest value in the table. The recursive subquery uses an to select the next lowest value until all unique values are retrieved. To get the number of distinct values in table test, you only need to count the number of values returned by the recursive CTE:
The recursive CTE has a low latency because it performs 10 limited scans of the index, each reading only one row and skipping the rest. You can see this using :
Because this pattern incurs the overhead of a new scan for each iteration, it is slower per row than a full scan. It is therefore faster than a full scan in cases (such as this one) where many rows are skipped, but is slower if they are not.
Some recursive CTEs are not not yet optimized.

Correlated common table expressions

If a common table expression is contained in a subquery, the CTE can reference columns defined outside of the subquery. This is called a correlated common table expression. For example, in the following query, the expression (SELECT 1 + x) references x in the outer scope.
CTEs containing statements (INSERT, UPSERT, UPDATE, DELETE) that modify data can appear only at the upper level, so they cannot be correlated.

See also