7.8.2. Data-Modifying Statements in
WITH
7.8.2.WITH中的数据修改语句
You can use data-modifying statements (
INSERT
,
UPDATE
, or
DELETE
) in
WITH
. This allows you
to perform several different operations in the same query. An example is:
可以在with中使用数据修改语句(INSERT,UPDATE或DELETE)。这样就可以在一个查询中执行多种不同的操作。例如:
WITH moved_rows AS (
DELETE FROM products
WHERE
"date" >= '2010-10-01' AND
"date" < '2010-11-01'
RETURNING *
)
INSERT INTO products_log
SELECT * FROM moved_rows;
This query effectively moves rows from
products
to
products_log
. The
DELETE
in
WITH
deletes the specified rows from
products
, returning their contents by means of its
RETURNING
clause; and then the primary query reads that output and inserts it into
products_log
.
此查询将products表中的行移动到products_log表中。WITH中的DELETE删掉products中指定的行,然后通过RETURNING子句返回删除的内容;然后主查询读取该输出并将其插入到products_log表中。
A fine point of the above example is that the
WITH
clause is attached to the
INSERT
, not the sub-
SELECT
within the
INSERT
. This is necessary because data-modifying statements are only allowed
in
WITH
clauses that are attached to the top-level statement. However, normal
WITH
visibility rules
apply, so it is possible to refer to the
WITH
statement's output from the sub-
SELECT
.
上面示例的一个优点是,WITH子句附加到INSERT,而不是INSERT中的SELECT子句。这是必需的,因为数据修改语句只允许附加在等层的WITH子句中。但是,通常会使用WITH可见性规则,因此可以从SELECT子句中引用WITH语句的输出。
Data-modifying statements in
WITH
usually have
RETURNING
clauses (see Section 6.4), as shown
in the example above. It is the output of the
RETURNING
clause,
not
the target table of the data-modifying
statement, that forms the temporary table that can be referred to by the rest of the query. If a
data-modifying statement in
WITH
lacks a
RETURNING
clause, then it forms no temporary table and
cannot be referred to in the rest of the query. Such a statement will be executed nonetheless. A notparticularly-
useful example is:
如上例所示,WITH中的数据修改语句通常具有RETURNING子句(请参见6.4节)。查询中引用的是REUTRNING子句的返回值,而不是数据定义语句的目标表。如果WITH中的数据修改语句缺少RETURNING子句,则它不形成临时表,并且在查询中不能被引用。尽管如此,仍将执行该语句。 一个不是特别有用的示例:
WITH t AS (
DELETE FROM foo
)
DELETE FROM bar;
This example would remove all rows from tables
foo
and
bar
. The number of affected rows reported
to the client would only include rows removed from
bar
.
上例会删掉foo和bar表中的所有行。而客户端看到的被影响的行仅仅来自于bar表。
Recursive self-references in data-modifying statements are not allowed. In some cases it is possible
to work around this limitation by referring to the output of a recursive
WITH
, for example:
数据修改语句中不能使用递归自调用。一些情况下,往往可以通过在外部调用with递归来折中解决此问题。例如:
WITH RECURSIVE included_parts(sub_part, part) AS (
SELECT sub_part, part FROM parts WHERE part = 'our_product'
UNION ALL
SELECT p.sub_part, p.part
FROM included_parts pr, parts p
WHERE p.part = pr.sub_part
)
DELETE FROM parts
WHERE part IN (SELECT part FROM included_parts);
This query would remove all direct and indirect subparts of a product.
此查询会删掉一个产品中所有直接或间接的部分。
Data-modifying statements in
WITH
are executed exactly once, and always to completion, independently
of whether the primary query reads all (or indeed any) of their output. Notice that this is different
from the rule for
SELECT
in
WITH
: as stated in the previous section, execution of a
SELECT
is
carried only as far as the primary query demands its output.
WITH中的数据修改语句仅执行一次,并且始终执行至完成,而与主查询是否读取其输出的全部(或确实读取)无关。请注意,这与WITH中的SELECT规则不同:如上一节所述,仅在主查询需要其输出时,才执行SELECT。
The sub-statements in
WITH
are executed concurrently with each other and with the main query.
Therefore, when using data-modifying statements in
WITH
, the order in which the specified updates
actually happen is unpredictable. All the statements are executed with the same
snapshot
(see Chapter
13), so they cannot “see” one another's effects on the target tables. This alleviates the effects of the
unpredictability of the actual order of row updates, and means that
RETURNING
data is the only way
to communicate changes between different
WITH
sub-statements and the main query. An example of
this is that in
WITH中的子语句彼此之间以及与主查询同时执行。因此,在WITH中使用数据修改语句时,指定更新实际发生的顺序是不可预测的。所有语句都使用相同的快照执行(请参见第13章),因此它们无法“看到”彼此对目标表的影响。这减轻了行更新实际顺序的不可预测性的影响,并且意味着RETURNING数据是在不同的WITH子语句与主查询之间传递变化的唯一途径。 例如:
WITH t AS (
UPDATE products SET price = price * 1.05
RETURNING *
)
SELECT * FROM products;
the outer
SELECT
would return the original prices before the action of the
UPDATE
, while in
外面的SELECT语句会返回在UPDATE之前的原始价格,而在:
WITH t AS (
UPDATE products SET price = price * 1.05
RETURNING *
)
SELECT * FROM t;
the outer
SELECT
would return the updated data.
外面的SELECT语句会返回更新后的价格。
Trying to update the same row twice in a single statement is not supported. Only one of the modifications
takes place, but it is not easy (and sometimes not possible) to reliably predict which one. This also
applies to deleting a row that was already updated in the same statement: only the update is performed.
Therefore you should generally avoid trying to modify a single row twice in a single statement. In
particular avoid writing
WITH
sub-statements that could affect the same rows changed by the main
statement or a sibling sub-statement. The effects of such a statement will not be predictable.
不支持在单个语句中尝试两次更新同一行。只有其中一个修改会成功,但无法准确预测是哪一个更改(有时甚至是不可能)。这也适用于在
同一条语句中
删除已更新的同一行:仅执行更新。因此,通常应避免在单个语句中尝试两次修改相同行。特别要避免编写WITH子语句及主语句会影响同一行的语句。这种语句的影响是不可预测的。
At present, any table used as the target of a data-modifying statement in
WITH
must not have a conditional
rule, nor an
ALSO
rule, nor an
INSTEAD
rule that expands to multiple statements.
当前,任何在WITH中用作数据修改语句目标的表都不得具有条件规、ALSO规则、扩展为多个语句的INSTEAD规则。