WITH query, provides a shorthand name to a possibly complex subquery before it is used in a larger query context. This improves the readability of SQL code.
You can use CTEs in combination with SELECT clauses and INSERT, DELETE, UPDATE, and UPSERT data-modifying statements.
For many workloads, CTEs are an effective alternative to temporary tables for intermediate results within a single statement. CTEs avoid the performance overhead of temp tables, and the optimizer can choose whether to materialize them.
Synopsis
Parameters
Overview
The examples on this page use MovR, a fictional vehicle-sharing application, to demonstrate CockroachDB SQL statements. To follow along, run
cockroach demo 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 MovR: A Global Vehicle-sharing App.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:
WITH clause defines the temporary name r for
the subquery over rides, and that name becomes a table name
for use in any table expression of the
subsequent SELECT clause.
This query is equivalent to, but simpler to read than:
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:
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:
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 data-modifying statement (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:
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 aWITH operator.
For example:
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:- Add the
RECURSIVEkeyword directly after theWITHoperator in the CTE definition, and before the CTE name. - Define an initial, non-recursive subquery. This subquery defines the initial values of the CTE.
- Add the
UNIONorUNION ALLkeyword after the initial subquery. TheUNIONvariant deduplicates rows. - Define a recursive subquery that references its own output. This subquery can also reference the CTE name, unlike the initial subquery.
- Write a parent query that evaluates the results of the CTE.
- The initial query is evaluated. Its results are stored as the result rows of the CTE and copied to a temporary, working table.
- If the working table is not empty, the recursive subquery is evaluated iteratively, using the contents of the working table for the self-reference. The results of each iteration replace the contents of the working table, and are also added to the result rows of the CTE. When the recursive subquery returns no rows, the working table is empty and iteration stops.
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: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 WHERE clause 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:
LIMIT 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:
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:EXPLAIN:
LIMIT and ORDER BY clauses to select the lowest value in the table. The recursive subquery uses an inner join 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:
EXPLAIN:
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.
INSERT, UPSERT, UPDATE, DELETE) that modify data can appear only at the upper level, so they cannot be correlated.

