↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

百科 / 执行计划节点 / 修改

ModifyTable

ModifyTable

执行计划指定的数据修改操作,并在请求时产生 RETURNING 行。

当前查看 PostgreSQL 18.6。

说明

执行计划指定的数据修改操作,并在请求时产生 RETURNING 行。

核心节点标签
T_ModifyTable
结构化 EXPLAIN 节点类型
ModifyTable
输入
一个或多个数据修改输入计划
输出
请求时输出 RETURNING 元组
执行器初始化函数
ExecInitModifyTable
内存机制
unclassified

EXPLAIN 名称与属性

结构化格式使用上述 Node Type。文本格式名称还可能包含操作、策略、连接类型、扫描方向或聚合阶段属性。

此源码记录的文本名称:Insert, Update, Delete, Merge.

并行感知与并行安全是不同的计划属性。在并行工作进程内运行的节点不一定是并行感知节点。

内存与临时存储

本次抽取不为此节点设定统一的内存上限或落盘策略。请查看同一构建的实现、相关表达式或提供方。

如果 FDW 支持批处理且请求了批处理,则累积行并分批插入,否则逐行插入。

初始化批次槽。由于不知道需要多少槽,随着批次增长逐步初始化,并在不同批次间保留。为缓解资源所有者处理大量引用对象时的低效情况,例如许多槽引用同一元组描述符,为每个槽复制相应的元组描述符。

并行执行与运行信息采集

以下源码回调可以协调执行或收集工作进程的测量数据。回调存在不代表该节点普遍支持共享并行扫描或共享状态。

此构建的回调:none extracted from this node implementation.

同版本手册说明

归并连接要求输入数据按连接键排序。在这个例子中,两个输入都通过索引扫描按正确顺序访问行而完成排序;不过也可以采用顺序扫描再排序的方式。(对于需要排序很多行的情况,顺序扫描加排序往往会胜过索引扫描,因为索引扫描需要非顺序磁盘访问。)

观察备选计划的一种方法,是利用 第 19.7.1 节 中描述的启用/禁用标志,强迫规划器忽略它认为最便宜的策略。(这是个粗糙但有用的工具。另见 第 14.3 节 。)例如,如果我们并不确信前一个示例中归并连接真的是最佳连接类型,可以试试:

这表明,规划器认为在这个场景下,哈希连接的代价几乎比归并连接高出 50%。当然,接下来的问题就是它是否判断正确。我们可以像 下文 所述,使用 EXPLAIN ANALYZE 来研究。

如本例所示,查询为 INSERT、UPDATE、DELETE 或 MERGE 命令时,实际应用表变更的工作由顶层 Insert、Update、Delete 或 Merge 计划节点完成。其下的计划节点负责定位旧行和/或计算新数据。因此,上例仍采用前面见过的位图表扫描,其输出传给 Update 节点,由后者存储更新后的行。虽然数据修改节点可能消耗相当多的运行时间(本例中占了绝大部分),但规划器目前并未将这部分工作计入代价估算。这是因为任何正确查询计划需要完成的这部分工作都相同,因此不会影响规划决策。

当 UPDATE 、 DELETE 或 MERGE 命令影响分区表或继承层次时,输出可能像这样:

在这个示例中,Update 节点需要考虑三个子表,而不包括最初提到的那个分区表,因为它本身并不存储任何数据。因此这里有三个输入扫描子计划,每个表对应一个。为了便于理解,Update 节点带有注释,显示将要更新的具体目标表,其顺序与对应子计划的顺序一致。

本版手册中的示例

示例摘自 PostgreSQL 18.6 手册;本百科未实际执行此示例。

另一种可能的连接方式是归并连接,如下所示:

EXPLAIN SELECT *
FROM tenk1 t1, onek t2
WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2;

                                        QUERY PLAN
------------------------------------------------------------------------------------------
 Merge Join  (cost=0.56..233.49 rows=10 width=488)
   Merge Cond: (t1.unique2 = t2.unique2)
   ->  Index Scan using tenk1_unique2 on tenk1 t1  (cost=0.29..643.28 rows=100 width=244)
         Filter: (unique1 < 100)
   ->  Index Scan using onek_unique2 on onek t2  (cost=0.28..166.28 rows=1000 width=244)

示例摘自 PostgreSQL 18.6 手册;本百科未实际执行此示例。

请记住,EXPLAIN ANALYZE 会实际执行查询,因此所有副作用都会照常发生,只是查询本应输出的结果被丢弃,改为打印 EXPLAIN 数据。如果希望分析修改数据的查询而不改变表,可以随后回滚命令,例如:

BEGIN;

EXPLAIN ANALYZE UPDATE tenk1 SET hundred = hundred + 1 WHERE unique1 < 100;

                                                           QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
 Update on tenk1  (cost=5.06..225.23 rows=0 width=0) (actual time=1.634..1.635 rows=0.00 loops=1)
   ->  Bitmap Heap Scan on tenk1  (cost=5.06..225.23 rows=100 width=10) (actual time=0.065..0.141 rows=100.00 loops=1)
         Recheck Cond: (unique1 < 100)
         Heap Blocks: exact=90
         Buffers: shared hit=4 read=2
         ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..5.04 rows=100 width=0) (actual time=0.031..0.031 rows=100.00 loops=1)
               Index Cond: (unique1 < 100)
               Index Searches: 1
               Buffers: shared read=2
 Planning Time: 0.151 ms
 Execution Time: 1.856 ms

ROLLBACK;

执行器实现说明

说明:ModifyTable 从 outerPlan 接收输入:INSERT 时为待插入数据;UPDATE 和 MERGE 时为修改列的新值及行定位信息;DELETE 时仅为行定位信息。

待修改关系可以是普通表、外部表或视图。如果是视图,要么具有足够的 INSTEAD OF 触发器,要么此节点只执行 MERGE ... DO NOTHING。若原始 MERGE 指向不属于这两类的视图,之前的处理已把 ModifyTable 的结果关系改为该视图的底层关系。此节点会处理 ri_WithCheckOptions,其中可能包含那些可自动更新视图的表达式。

MERGE 对源关系和目标表执行连接。若包含 WHEN NOT MATCHED [BY TARGET] 子句,则使用可能输出没有匹配目标元组的外连接。此时,未匹配目标元组的行定位信息为 NULL,只能执行 INSERT。对于匹配的目标元组,使用行定位信息确定要 UPDATE 或 DELETE 的元组。若所有子句均为 WHEN MATCHED 或 WHEN NOT MATCHED BY SOURCE,连接输出的所有元组都包含匹配的目标元组,因此都有行定位信息。

如果查询指定了 RETURNING,ModifyTable 每完成一行的插入、更新或删除后就返回一个 RETURNING 元组,需要再次调用才能继续执行。如果没有 RETURNING,则在节点内部循环,直到全部工作完成,再返回 NULL,从而避免无用的调用/返回开销。

ModifyTable 操作的上下文结构体,包含基本执行状态,以及由 ExecUpdateAct() 和 ExecDeleteAct() 填充、用于向调用者报告执行结果的若干输出变量。

核心源码中的 EXPLAIN 标识

case T_ModifyTable:
			sname = "ModifyTable";
			switch (((ModifyTable *) plan)->operation)
			{
				case CMD_INSERT:
					pname = operation = "Insert";
					break;
				case CMD_UPDATE:
					pname = operation = "Update";
					break;
				case CMD_DELETE:
					pname = operation = "Delete";
					break;
				case CMD_MERGE:
					pname = operation = "Merge";
					break;
				default:
					pname = "???";
					break;
			}
			break;

本构建中的 EXPLAIN 标签

文本格式标签结构化节点标识
InsertModifyTable
UpdateModifyTable
DeleteModifyTable
MergeModifyTable

相关条目

文档与源码

来源构建
版本
18.6
构建
PostgreSQL 18.6 source archive
来源指纹
555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f

版本比较

PostgreSQL 14 → 15: 属性变化。

以下差异保留原始字段名与英文源描述。

--- PostgreSQL 14
+++ PostgreSQL 15
@@ -8,6 +8,7 @@
   "text_names": [
     "Insert",
     "Update",
-    "Delete"
+    "Delete",
+    "Merge"
   ]
 }

比较已记录的接口与属性,排除来源指纹和构建元数据。某个样本中没有记录,不能据此判断实际引入或移除的版本。

导出 JSON · 返回执行计划节点 · 收录范围为 PostgreSQL 10 至 20;最早采样版本不一定是实际引入版本。