Project: YugabyteDB (App Definition and Development / Database)
File: with-clause-syntax-semantics.md

title: WITH clauseSQL syntax and semantics linkTitle: WITH clauseSQL syntax and semantics headerTitle: WITH clauseSQL syntax and semantics description: This section specifies the syntax and semantics of the WITH clause menu: v2.18: identifier: with-clause-syntax-semantics parent: with-clause weight: 20 type: docs The and diagrams are reproduced from the section that describes the . {{%ebnf%}} with_clause, commontableexpression {{%/ebnf%}} The `WITH` clause lets you name a SQL statement which might be one of , , , , or . You can then refer to the statement by name (just as if it were a schema-level view that names a `SELECT` statement) in a subsequent CTE definition or in the statement's final section. A very common use of the data-changing statements in the `WITH` clause is when they have a `RETURNING` clause. Then when you later refer to that statement by name, it behaves the same as if it were a named `SELECT` statement. The uniqueness scope for the name of the CTE is the relation names defined in a particular `WITH` clause. You can define column aliases compactly in the optional parenthesized list that follows the name of the CTE, just as you can with a schema-level view. See the section . Notice that the `WITH` clause is legal in the `SELECT` statement and in each of the kinds of data-changing statements, but not in the `VALUES` statement. See the syntax diagrams for , , , , and . The recursive CTE is explained in a . First, create some test tables and inspect the contents. ```plpgsql set clientminmessages = warning; do $body$ declare table_names constant text[] := array['t1', 't2', 't3']; drop_table constant text := ' drop table if exists ? cascade'; create_table constant text := ' create table ?(k int primary key, v int)'; insert_table constant text := ' insert into ?(k, v) select g.v, g.v*2 from generate_series($1, $2) as g(v)'; t text not null := ''; i int not null := 1; n constant int := 2; begin foreach t in array table_names loop execute replace(drop_table, '?', t); execute replace(create_table, '?', t); execute replace(insert_table, '?', t) using i, i + n; i := i + n + 1; end loop; end; $body$; select 't1'::text as name, k, v from t1 union all select 't2'::text as name, k, v from t2 union all select 't3'::text as name, k, v from t3 order by 1, 2; ``` This is the result: ``` name | k | v ++- t1 | 1 | 2 t1 | 2 | 4 t1 | 3 | 6 t2 | 4 | 8 t2 | 5 | 10 t2 | 6 | 12 t3 | 7 | 14 t3 | 8 | 16 t3 | 9 | 18 ``` Now execute the example query. Notice that the `WITH` clause defines an `INSERT` CTE, an `UPDATE` CTE, a `DELETE` CTE, and a `SELECT` CTE. Each of the data-changing CTEs has a `RETURNING` clause; and the `SELECT` CTE accesses the unions returned by each of

```plpgsql with i as ( insert into t1(k, v) values (21, 17), (31, 42) returning k, v), u as ( update t2 set v = 99 where k in (4, 5) returning k, v), d as ( delete from t3 where k in (8, 9) returning k, v), s as ( select 'inserted into t1'::text as action, k, v from i union all select 'udated in t2'::text as action, k, v from u union all select 'deleted from t3'::text as action, k, v from d) select action, k, v from s order by k; ``` This is the result: ``` action | k | v +-+- udated in t2 | 4 | 99 udated in t2 | 5 | 99 deleted from t3 | 8 | 16 deleted from t3 | 9 | 18 inserted into t1 | 21 | 17 inserted into t1 | 31 | 42 ``` Check that the result is as expected in the affected tables, "t1", "t2", and "t3": ```plpgsql select 't1'::text as name, k, v from t1 union all select 't2'::text as name, k, v from t2 union all select 't3'::text as name, k, v from t3 order by 1, 2; ``` This is the result: ``` name | k | v +-+- t1 | 1 | 2 t1 | 2 | 4 t1 | 3 | 6 t1 | 21 | 17 t1 | 31 | 42 t2 | 4 | 99 t2 | 5 | 99 t2 | 6 | 12 t3 | 7 | 14 ``` First, clear out the data from table "t1" from the previous demonstration. ```plpgsql truncate table t1; ``` Now use a `WITH` clause as part of an `INSERT` statement. ```plpgsql with a1 as ( select g.v as k, g.v*2 as v from generate_series(1, 3) as g(v)), a2 as ( values (10, 17), (11, 42), (12, 99)) insert into t1(k, v) select k, v from a1 union all select column1 as k, column2 as v from a2; select k, v from t1 order by k; ``` This is the result: ``` k | v -+- 1 | 2 2 | 4 3 | 6 10 | 17 11 | 42 12 | 99 ``` The following SQL statement uses two `WITH` clauses. Each defines its own scope for the names of the CTEs that they define. Notice that the name "colliding", as the name of a relation, is defined in three different scopes: schema scope; the scope of the outer `WITH` clause; and the scope of the inner `WITH` clause that the outer one begins with. For good measure, it's also used in a different namespace: as the name of a column, or column alias. The outer `WITH` clause simply cannot see the names that are defined in the inner `WITH` clause. But each of these `WITH` clauses can see a schema-level relation with a name that collides with a name defined in that `WITH` clause by qualifying it with the schema name. Column names are always defined within the scope of the relation that contains them. The example assumes that a user called "u1" exists and that a database called "demo" exists. Change these names in the example to match the names that are available to you. ```plpgsql \c demo u1 set clientminmessages = warning; drop table if exists colliding cascade; create table u1.colliding(colliding text primary key); insert into u1.colliding(colliding) values ('goodbye'), ('world'); with colliding(colliding) as ( with a(colliding) as (select 'Hello'), colliding(colliding) as (select colliding from u1.colliding where colliding like 'w%') select ((select colliding from a)||' '||(select colliding from colliding)) ) select (select colliding from