DELETE — 删除表中的行
[ WITH [ RECURSIVE ]with_query[, ...] ] DELETE FROM [ ONLY ]table_name[ * ] [ FOR PORTION OFrange_column_namefor_portion_of_target] [ [ AS ]alias] [ USINGfrom_item[, ...] ] [ WHEREcondition| WHERE CURRENT OFcursor_name] [ RETURNING [ WITH ( { OLD | NEW } ASoutput_alias[, ...] ) ] { * |output_expression[ [ AS ]output_name] } [, ...] ] 其中for_portion_of_target为: { FROMstart_timeTOend_time| (portion) }
DELETE从指定表中删除满足 WHERE子句的行。如果省略WHERE 子句,效果就是删除表中的所有行。结果是一个合法但为空的表。
TRUNCATE提供了一种更快的机制来移除表中的所有行。
有两种方法可以利用数据库中其他表所包含的信息来删除一个表中的行: 使用子选择,或者在USING子句中指定附加表。 哪种技术更合适取决于具体情况。
可选的RETURNING子句使DELETE 基于每个实际被删除的行计算并返回一个或多个值。可以计算使用该表 列和/或USING中提到的其他表列的任意表达式。 RETURNING列表的语法与SELECT 的输出列表相同。
如果使用了FOR PORTION OF子句,那么删除只会影响与给定片段重叠的行。 此外,如果某一行的应用时间超出了FOR PORTION OF边界,那么删除只会更改这些边界内的应用时间。 实际上,只有FOR PORTION OF所针对的历史会被删除,边界之外的时刻不会被删除。 另外,在一行被删除之后,可能会插入新的时态残留: 这些行的范围或多范围会接收目标边界之外剩余的应用时间,而其他列保留原值。 对于范围列,插入记录的数量可以是零到两条,取决于原始应用时间是被完全删除、在变更前后扩展,还是两者都有。 多范围永远不需要两个时态残留,因为一个值总可以容纳剩余的任何应用时间。
要从一个表中删除行,必须具有该表上的DELETE权限, 以及USING子句中出现的任何表或者其值会在 condition中读取的任 何表上的SELECT权限。 如果使用了FOR PORTION OF,那么二次插入不需要该表上的 INSERT权限。(这是因为概念上并没有添加任何新信息; 插入的行只是保留未被针对的时间段中的现有数据。)
with_query #WITH子句允许你指定一个或多个子查询,这些子查 询可在DELETE查询中按名称引用。详见 Section 7.8和SELECT。
table_name #要从中删除行的表名(可以是模式限定的)。如果在表名前指定了 ONLY,只会从所提及表中删除匹配行。如果未指定 ONLY,还会删除继承自该表的任何表中的匹配行。 可选地,可以在表名后指定*,以显式指示包含后代 表。
alias #目标表的替代名称。提供别名时,它会完全隐藏该表的实际名称。 例如,给定DELETE FROM foo AS f, DELETE语句的其余部分必须将该表称为 f,而不是foo。
range_column_name #在执行时态删除时要使用的范围或多范围列。
for_portion_of_target #要删除的片段。如果针对的是范围列,它可以采用 FROM start_time TO end_time 的形式。否则,它必须采用 (portion) 的形式,其中portion是一个表达式, 其结果值类型与range_column_name 的类型相同。
start_time #在时态删除中要更改的最早时间(含)。这必须是与 range_column_name中的 范围基类型匹配的值。这里的空值表示删除的起点没有上界(与范围类型一样)。
end_time #在时态删除中要更改的最晚时间(不含)。这必须是与 range_column_name中的 范围基类型匹配的值。这里的空值表示删除的终点没有上界(与范围类型一样)。
from_item #一个表表达式,允许其他表的列出现在WHERE条件 中。这里使用的语法与SELECT语句的 FROM子句相同; 例如,可以为表名指定别名。除非你打算进行自连接(这种情况下它 必须在from_item中带有别名出现),否 则不要把目标表重复写成from_item。
condition #一个返回boolean类型值的表达式。只有使这个表达式返 回true的行才会被删除。
cursor_name #要在WHERE CURRENT OF条件中使用的游标名。 要被删除的行是最近一次从该游标中取出的那一行。该游标必须是针 对DELETE目标表的非分组查询。注意, WHERE CURRENT OF不能与布尔条件同时指定。有 关将游标用于WHERE CURRENT OF的更多信息,请见 DECLARE。
output_alias #在RETURNING列表中为OLD或 NEW行指定的可选替代名称。
默认情况下,可通过写 OLD. 或column_nameOLD.*返回目标表旧值,通过写 NEW. 或column_nameNEW.*返回新值。提供别名后,这些名称会被隐藏, 必须使用别名来引用旧行或新行。例如 RETURNING WITH (OLD AS o, NEW AS n) o.*, n.*。
output_expression #在每一行被删除后,由DELETE命令计算并返回的 表达式。该表达式可以使用由table_name 命名的表或USING中列出的表的任何列名。写成 *可返回所有列。
列名或*可以用OLD、 NEW或对应的 output_alias进行限定, 以返回旧值或新值。未限定的列名或*,以及用目标 表名称或别名限定的列名或*,都会返回旧值。
对于简单的DELETE,所有新值都将为 NULL。但是,如果某个ON DELETE 规则导致转而执行INSERT或UPDATE, 那么新值可能为非NULL。
output_name #用于返回列的名称。
在成功完成时,一个DELETE命令会返回以下形式 的命令标签:
DELETE count
count是被删除的行数。 注意,当删除被BEFORE DELETE触发器抑制时,这个 数量可能小于匹配condition 的行数。如果count为 0, 则该查询没有删除任何行(这不被视为错误)。 如果使用了FOR PORTION OF,那么 count不包含已插入的时态残留。
如果DELETE命令包含RETURNING 子句,其结果将类似于一个SELECT语句,其中包含 RETURNING列表中定义的列和值,并在该命令删除的 行上进行计算。如果使用了FOR PORTION OF,那么 RETURNING子句会为每个被删除的行返回一条结果,但不包含插入的 时态残留。 应用时间列的值与被删除行的旧值一致。请注意,如果插入了时态残留, 这将表示比实际删除的更多的应用时间。
PostgreSQL允许通过在 USING子句中指定其他表,在 WHERE条件中引用这些表的列。例如,要删除由给定 制片人制作的所有电影,可以这样做:
DELETE FROM films USING producers WHERE producer_id = producers.id AND producers.name = 'foo';
本质上,这里发生的是在films和 producers之间进行连接,并将所有成功连接 到的films行标记为删除。这种语法不是标准 的。更标准的写法是:
DELETE FROM films WHERE producer_id IN (SELECT id FROM producers WHERE name = 'foo');
在某些情况下,连接形式比子选择形式更容易书写或者执行更快。
当使用FOR PORTION OF时,这可能会导致没有 INSERT权限的用户触发INSERT触发器。 在使用SECURITY DEFINER触发器函数时应考虑这一点。
删除所有电影,但音乐剧除外:
DELETE FROM films WHERE kind <> 'Musical';
清空表films:
DELETE FROM films;
删除已完成的任务,并返回被删除行的完整详情:
DELETE FROM tasks WHERE status = 'DONE' RETURNING *;
删除游标c_tasks当前所定位的 tasks行:
DELETE FROM tasks WHERE CURRENT OF c_tasks;
一个时态删除的示例:
DELETE FROM products FOR PORTION OF valid_at FROM '2021-08-01' TO '2023-09-01' WHERE product_no = 5;
虽然DELETE没有LIMIT子句, 但可以采用UPDATE文档 中介绍的相同方法来获得类似效果:
WITH delete_batch AS (
SELECT l.ctid FROM user_logs AS l
WHERE l.status = 'archived'
ORDER BY l.creation_date
FOR UPDATE
LIMIT 10000
)
DELETE FROM user_logs AS dl
USING delete_batch AS del
WHERE dl.ctid = del.ctid;
这里使用ctid之所以安全,仅仅是因为该查询 会被重复执行,从而避免了ctid发生变化的 问题。
这个命令符合SQL标准,不过 USING和RETURNING子句是 PostgreSQL扩展,在 DELETE中使用WITH也是扩展。