{"Entry":{"collection":"plan","key":"append","name":"Append","aliases":["Append","T_Append"],"metadata":{"aliases":["Append","T_Append"],"category":"Combination","content_hash":"7b84df68f27b010d9415995931cc4f4eeee175de068b68a7ee3bf105e4e15287","derived_content_hash":"01a836a5fbb90ee25aa65e06ab608c7eaa331e23e242ffe42b90538d42db9224","imported_at":"2026-09-30T00:40:44.039069+08:00","name":"Append","name_zh":"Append","slug":"append","summary":"Visits its child plans and combines their rows."}},"Definition":{"Collection":"plan","Key":"append","SourceDatabase":"center","Version":"18","SourceTable":"plan_node","SourceKey":"append","SourceRevision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","Facts":{"comparison_data":{"initializer":"ExecInitAppend","memory_mechanism":"unclassified","node_tag":"T_Append","parallel_callbacks":["ExecAppendEstimate","ExecAppendInitializeDSM","ExecAppendInitializeWorker","ExecAppendReInitializeDSM"],"partial_modes":[],"strategies":[],"text_names":["Append"]},"comparison_hash":"05fc087ef5a99b9d2d14f9b7e397d941085dafd8c263de24871ec9926b47ad8f","description":["Visits its child plans and combines their rows."],"description_zh":"依次访问子计划并合并其输出行。","description_zh_html":"\u003cp\u003e依次访问子计划并合并其输出行。\u003c/p\u003e\u003cp\u003e核心节点标识：\u003ccode\u003eT_Append\u003c/code\u003e；EXPLAIN 名称：\u003ccode\u003eAppend\u003c/code\u003e。\u003c/p\u003e\u003cp\u003e本版本源码中的并行执行或观测回调：\u003ccode\u003eExecAppendEstimate, ExecAppendInitializeDSM, ExecAppendInitializeWorker, ExecAppendReInitializeDSM\u003c/code\u003e。回调的存在不表示该节点必然支持共享并行扫描；在并行工作进程中执行也不等于具有并行感知能力。\u003c/p\u003e\u003ch3\u003e同版本中文手册节选\u003c/h3\u003e\u003cp\u003e\n\u003ccode class=\"command\"\u003eEXPLAIN ANALYZE\u003c/code\u003e 测得的运行时间，可能以两种重要方式偏离同一查询的正常执行时间。第一，由于不会向客户端传递任何输出行，因此网络传输开销不会被计入。除非指定了 \u003ccode class=\"literal\"\u003eSERIALIZE\u003c/code\u003e，I/O 转换开销同样也不会被计入。第二，\u003ccode class=\"command\"\u003eEXPLAIN ANALYZE\u003c/code\u003e 附加的测量开销本身可能相当可观，尤其是在操作系统调用 \u003ccode class=\"function\"\u003egettimeofday()\u003c/code\u003e 较慢的机器上。可以使用 \u003ca class=\"xref\" href=\"/zh/docs/18/pgtesttiming.html\"\u003e\u003cspan class=\"refentrytitle\"\u003e\u003cspan class=\"application\"\u003epg_test_timing\u003c/span\u003e\u003c/span\u003e\u003c/a\u003e 工具来测量系统上的计时开销。\n   \u003c/p\u003e\u003cp\u003e\n    不应将 \u003ccode class=\"command\"\u003eEXPLAIN\u003c/code\u003e 结果外推到与实际测试场景差异很大的情况。例如，在一个很小的表上得到的结果，不能假定也适用于大型表。规划器的代价估计不是线性的，因此它可能会为更大或更小的表选择不同的计划。一个极端示例是：对于只占用一个磁盘页的表，无论索引是否可用，几乎总会得到顺序扫描计划。规划器认识到，无论如何处理该表都需要一次磁盘页读取，因此再额外读取页面去查看索引并没有价值。（前面的 \u003ccode class=\"literal\"\u003epolygon_tbl\u003c/code\u003e 示例已经展示过这种情况。）\n   \u003c/p\u003e\u003cp\u003e\n    在某些情况下，实际值和估计值不会很好地对应，但这并不表示出现了问题。其中一种情况是，计划节点的执行会被 \u003ccode class=\"literal\"\u003eLIMIT\u003c/code\u003e 或类似效果提前截断。例如，在前面用过的 \u003ccode class=\"literal\"\u003eLIMIT\u003c/code\u003e 查询中：\n\n\u003c/p\u003e\u003cp\u003e\n\n    Index Scan 节点的估计代价和行计数显示得像是它会运行到结束一样。但实际上，Limit 节点在拿到两行之后就停止请求更多行，因此实际行计数只有 2，运行时间也低于代价估计所暗示的水平。这并不是估计错误，只是估计值与真实值的显示方式不同而已。\n   \u003c/p\u003e\u003cp\u003e\u003ca href=\"/zh/docs/18/using-explain.html#USING-EXPLAIN-CAVEATS\"\u003e查看完整中文手册与上下文\u003c/a\u003e。\u003c/p\u003e\u003cp\u003e本页开头的中文说明为 PG.CENTER 对编辑说明的翻译；手册节选保留对应版本中文文档原文。源码信息及手册示例不代表重新进行过运行测试。\u003c/p\u003e","evidence_kind":"source and documentation","explain_names":["Append"],"explain_prefixes":["Parallel","Async"],"facts":[{"label":"Core node tag","value":"T_Append"},{"label":"Structured EXPLAIN Node Type","value":"Append"},{"label":"Inputs","value":"Multiple child plans"},{"label":"Output","value":"Combined tuples"},{"label":"Executor initializer","value":"ExecInitAppend"},{"label":"Memory mechanism","value":"unclassified"}],"facts_zh":[{"label":"核心节点标签","value":"T_Append"},{"label":"结构化 EXPLAIN 节点类型","value":"Append"},{"label":"输入","value":"多个子计划"},{"label":"输出","value":"合并后的元组"},{"label":"执行器初始化函数","value":"ExecInitAppend"},{"label":"内存机制","value":"unclassified"}],"memory":{"description":"This extraction does not assign a universal memory limit or spill policy to this node. Inspect the same-build implementation and its expressions or provider.","evidence":[{"archive_sha256":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","label":"src/backend/executor/nodeAppend.c","path":"src/backend/executor/nodeAppend.c","sha256":"bfe49b7e44ce4c31878392c3d84eb5c6fa97a5185a39aca44ff1914033a84ad3","url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"}],"mechanism":"unclassified","source_notes":[]},"node_tag":"T_Append","parallel_callbacks":["ExecAppendEstimate","ExecAppendInitializeDSM","ExecAppendInitializeWorker","ExecAppendReInitializeDSM"],"partial_modes":[],"related":[{"label":"EXPLAIN","url":"/wiki/sql/explain/?v=18"},{"label":"Using EXPLAIN","url":"/docs/18/using-explain.html"},{"label":"Parallel plans","url":"/docs/18/parallel-plans.html"},{"label":"enable_parallel_append","url":"/wiki/guc/enable_parallel_append/?v=18"},{"label":"enable_async_append","url":"/wiki/guc/enable_async_append/?v=18"}],"release":{"channel":"stable","label":"18.6","major":"18","ref":"PostgreSQL 18.6 source archive","revision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","source_snapshot_utc":"","source_url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},"runtime_verified":false,"sections":[{"paragraphs":["Structured formats use the Node Type above. Text-format spellings can also include operation, strategy, join type, scan direction or aggregation-stage attributes.","Text names recorded by this source: Append.","Parallel-aware and parallel-safe are different plan properties. A node running inside a parallel worker is not necessarily a parallel-aware node."],"title":"EXPLAIN names and attributes"},{"paragraphs":["This extraction does not assign a universal memory limit or spill policy to this node. Inspect the same-build implementation and its expressions or provider."],"title":"Memory and temporary storage"},{"paragraphs":["The source callbacks below can coordinate execution or collect worker instrumentation. Their presence is not a blanket claim that this node supports a shared parallel scan or shared state.","Callbacks in this build: ExecAppendEstimate, ExecAppendInitializeDSM, ExecAppendInitializeWorker, ExecAppendReInitializeDSM."],"title":"Parallel execution and instrumentation"},{"paragraphs":["Normally, EXPLAIN will display every plan node created by the planner. However, there are cases where the executor can determine that certain nodes need not be executed because they cannot produce any rows, based on parameter values that were not available at planning time. (Currently this can only happen for child nodes of an Append or MergeAppend node that is scanning a partitioned table.) When this happens, those plan nodes are omitted from the EXPLAIN output and a Subplans Removed: N annotation appears instead.","Whenever PostgreSQL needs to combine rows from multiple sources into a single result set, it uses an Append or MergeAppend plan node. This commonly happens when implementing UNION ALL or when scanning a partitioned table. Such nodes can be used in parallel plans just as they can in any other plan. However, in a parallel plan, the planner may instead use a Parallel Append node.","When an Append node is used in a parallel plan, each process will execute the child plans in the order in which they appear, so that all participating processes cooperate to execute the first child plan until it is complete and then move to the second plan at around the same time. When a Parallel Append is used instead, the executor will instead spread out the participating processes as evenly as possible across its child plans, so that multiple child plans are executed simultaneously. This avoids contention, and also avoids paying the startup cost of a child plan in those processes that never execute it.","Also, unlike a regular Append node, which can only have partial children when used within a parallel plan, a Parallel Append node can have both partial and non-partial child plans. Non-partial children will be scanned by only a single process, since scanning them more than once would produce duplicate results. Plans that involve appending multiple result sets can therefore achieve coarse-grained parallelism even when efficient partial plans are not available. For example, consider a query against a partitioned table that can only be implemented efficiently by using an index that does not support parallel scans. The planner might choose a Parallel Append of regular Index Scan plans; each individual index scan would have to be executed to completion by a single process, but different scans could be performed at the same time by different processes."],"title":"Same-version manual discussion"},{"blocks":[{"code":"EXPLAIN UPDATE gtest_parent SET f1 = CURRENT_DATE WHERE f2 = 101;\n\n                                       QUERY PLAN\n----------------------------------------------------------------------------------------\n Update on gtest_parent  (cost=0.00..3.06 rows=0 width=0)\n   Update on gtest_child gtest_parent_1\n   Update on gtest_child2 gtest_parent_2\n   Update on gtest_child3 gtest_parent_3\n   -\u003e  Append  (cost=0.00..3.06 rows=3 width=14)\n         -\u003e  Seq Scan on gtest_child gtest_parent_1  (cost=0.00..1.01 rows=1 width=14)\n               Filter: (f2 = 101)\n         -\u003e  Seq Scan on gtest_child2 gtest_parent_2  (cost=0.00..1.01 rows=1 width=14)\n               Filter: (f2 = 101)\n         -\u003e  Seq Scan on gtest_child3 gtest_parent_3  (cost=0.00..1.01 rows=1 width=14)\n               Filter: (f2 = 101)","paragraphs":["Example copied from the PostgreSQL 18.6 manual; it was not executed for this collection.","When an UPDATE , DELETE , or MERGE command affects a partitioned table or inheritance hierarchy, the output might look like this:"],"source":{"label":"PostgreSQL 18.6 · using-explain","path":"using-explain.html","sha256":"60040c30180093418a0affe56dd27dff9df2504b705b38039589e458bf5c31ed","url":"/docs/18/using-explain.html#USING-EXPLAIN-ANALYZE"}}],"title":"Examples from this manual build"},{"paragraphs":["NOTES Each append node contains a list of one or more subplans which must be iteratively processed (forwards or backwards). Tuples are retrieved by executing the 'whichplan'th subplan until the subplan stops returning tuples, at which point that plan is shut down and the next started up.","Append nodes don't make use of their left and right subtrees, rather they maintain a list of subplans so a typical append node looks like this in the plan tree:","... / Append -------+------+------+--- nil / \\ | | | nil nil ... ... ... subplans","Append nodes are currently used for unions, and to support inheritance queries, where several relations need to be scanned. For example, in our standard person/student/employee/student-emp example, where student and employee inherit from person and student-emp inherits from student and employee, the query:","| Append -------+-------+--------+--------+ / \\ | | | | nil nil Scan Scan Scan Scan | | | | person employee student student-emp"],"title":"Executor implementation notes"},{"code":"case T_Append:\n\t\t\tpname = sname = \"Append\";\n\t\t\tbreak;","title":"EXPLAIN identity in core source"}],"sections_zh":[{"paragraphs":["结构化格式使用上述 Node Type。文本格式名称还可能包含操作、策略、连接类型、扫描方向或聚合阶段属性。","此源码记录的文本名称：Append.","并行感知与并行安全是不同的计划属性。在并行工作进程内运行的节点不一定是并行感知节点。"],"title":"EXPLAIN 名称与属性"},{"paragraphs":["本次抽取不为此节点设定统一的内存上限或落盘策略。请查看同一构建的实现、相关表达式或提供方。"],"title":"内存与临时存储"},{"paragraphs":["以下源码回调可以协调执行或收集工作进程的测量数据。回调存在不代表该节点普遍支持共享并行扫描或共享状态。","此构建的回调：ExecAppendEstimate, ExecAppendInitializeDSM, ExecAppendInitializeWorker, ExecAppendReInitializeDSM."],"title":"并行执行与运行信息采集"},{"paragraphs":["通常 EXPLAIN 显示规划器创建的每个节点。但执行器有时可以根据规划阶段尚不可知的参数值，判定某些节点无法产生任何行，因此不必执行。（目前仅会发生在扫描分区表的 Append 或 MergeAppend 节点的子节点上。）此时 EXPLAIN 省略这些节点，改为显示 Subplans Removed: N。","每当 PostgreSQL 需要将来自多个源的行合并成一个结果集时，它就会使用 Append 或 MergeAppend 计划节点。这种情况常见于实现 UNION ALL 或扫描分区表时。这样的节点和其他任何计划中的情形一样，也可以用于并行计划。不过，在并行计划中，规划器也可能改用 Parallel Append 节点。","当 Append 节点用于并行计划时，每个进程都会按照子计划出现的顺序执行它们，因此所有参与进程会协作执行第一个子计划直到其完成，然后再大致同时转向第二个子计划。而当使用 Parallel Append 时，执行器会尽可能均匀地将参与进程分散到各个子计划上，从而使多个子计划能够同时执行。这样既避免了争用，也避免让那些从未执行某个子计划的进程承担其启动代价。","此外，与在并行计划中只能包含部分子计划的普通 Append 节点不同，Parallel Append 节点既可包含部分子计划，也可包含非部分子计划。非部分子计划只由一个进程扫描，因为多次扫描会产生重复结果。因此，即使没有高效的部分计划，拼接多个结果集的计划也能实现粗粒度并行。例如，针对分区表的查询，可能只有借助不支持并行扫描的索引才能高效执行。规划器可能选择使用 Parallel Append 拼接多个普通 Index Scan 计划；每次索引扫描都必须由单个进程完整执行，但不同扫描可以由不同进程同时执行。"],"title":"同版本手册说明"},{"blocks":[{"code":"EXPLAIN UPDATE gtest_parent SET f1 = CURRENT_DATE WHERE f2 = 101;\n\n                                       QUERY PLAN\n----------------------------------------------------------------------------------------\n Update on gtest_parent  (cost=0.00..3.06 rows=0 width=0)\n   Update on gtest_child gtest_parent_1\n   Update on gtest_child2 gtest_parent_2\n   Update on gtest_child3 gtest_parent_3\n   -\u003e  Append  (cost=0.00..3.06 rows=3 width=14)\n         -\u003e  Seq Scan on gtest_child gtest_parent_1  (cost=0.00..1.01 rows=1 width=14)\n               Filter: (f2 = 101)\n         -\u003e  Seq Scan on gtest_child2 gtest_parent_2  (cost=0.00..1.01 rows=1 width=14)\n               Filter: (f2 = 101)\n         -\u003e  Seq Scan on gtest_child3 gtest_parent_3  (cost=0.00..1.01 rows=1 width=14)\n               Filter: (f2 = 101)","paragraphs":["示例摘自 PostgreSQL 18.6 手册；本百科未实际执行此示例。","当 UPDATE 、 DELETE 或 MERGE 命令影响分区表或继承层次时，输出可能像这样："],"source":{"label":"PostgreSQL 18.6 · using-explain","path":"using-explain.html","sha256":"60040c30180093418a0affe56dd27dff9df2504b705b38039589e458bf5c31ed","url":"/docs/18/using-explain.html#USING-EXPLAIN-ANALYZE"}}],"title":"本版手册中的示例"},{"paragraphs":["说明：每个 Append 节点包含一个或多个必须逐一处理的子计划，方向可以正向或反向。执行第 whichplan 个子计划取回元组，直到它不再返回元组，然后关闭该计划并启动下一个。","Append 节点不使用左、右子树，而是维护一个子计划列表，因此典型的 Append 节点在计划树中如下所示：","... / Append -------+------+------+--- nil / \\ | | | nil nil ... ... ... subplans","Append 节点目前用于并集操作，也用于需要扫描多个关系的继承查询。例如，在标准的 person/student/employee/student-emp 示例中，student 和 employee 继承自 person，而 student-emp 同时继承自 student 和 employee，查询如下：","| Append -------+-------+--------+--------+ / \\ | | | | nil nil Scan Scan Scan Scan | | | | person employee student student-emp"],"title":"执行器实现说明"},{"code":"case T_Append:\n\t\t\tpname = sname = \"Append\";\n\t\t\tbreak;","title":"核心源码中的 EXPLAIN 标识"}],"source_inventory":{"executor":"src/backend/executor/execProcnode.c","explain":"src/backend/commands/explain.c","implementation":"src/backend/executor/nodeAppend.c"},"sources":[{"archive_sha256":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","label":"src/backend/commands/explain.c:1406","line":1406,"path":"src/backend/commands/explain.c","sha256":"34c86d6070224a0e981efef51f79101d6d505e5874f1684ace183034bab14bb4","url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},{"archive_sha256":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","label":"src/backend/executor/execProcnode.c:181","line":181,"path":"src/backend/executor/execProcnode.c","sha256":"f8a06a3f539077249b20664b2812433db6d7bd12b2c0ca633525db43d06f112a","url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},{"archive_sha256":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","label":"src/backend/executor/nodeAppend.c","path":"src/backend/executor/nodeAppend.c","sha256":"bfe49b7e44ce4c31878392c3d84eb5c6fa97a5185a39aca44ff1914033a84ad3","url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},{"archive_sha256":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","label":"src/include/nodes/plannodes.h","path":"src/include/nodes/plannodes.h","sha256":"52422b327a8049fbbb20d8b96008a0fc0a6fafa60f7eff3c695d5b2e83830120","url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},{"label":"PostgreSQL 18.6 · using-explain","path":"using-explain.html","sha256":"60040c30180093418a0affe56dd27dff9df2504b705b38039589e458bf5c31ed","url":"/docs/18/using-explain.html#USING-EXPLAIN-CAVEATS"},{"label":"PostgreSQL 18.6 · parallel-plans","path":"parallel-plans.html","sha256":"62207d207bead82b01b59dc119c4f95856f08655cc11d4699a05a40867ed2070","url":"/docs/18/parallel-plans.html#PARALLEL-APPEND"},{"label":"PostgreSQL 18.6 · using-explain","path":"using-explain.html","sha256":"60040c30180093418a0affe56dd27dff9df2504b705b38039589e458bf5c31ed","url":"/docs/18/using-explain.html#USING-EXPLAIN-ANALYZE"}],"strategies":[],"tables":[{"columns":[{"key":"label","label":"Text-format label"},{"key":"identity","label":"Structured node identity"}],"key":"explain-labels","rows":[{"identity":"Append","label":"Append"}],"title":"EXPLAIN labels in this source build"}],"tables_zh":[{"columns":[{"key":"label","label":"文本格式标签"},{"key":"identity","label":"结构化节点标识"}],"key":"explain-labels","rows":[{"identity":"Append","label":"Append"}],"title":"本构建中的 EXPLAIN 标签"}],"zh_from":{"authored_translation":"authored-translation-of-reviewed-guidance","body_sha256":"02f08142cdfe4927912ab020bcf445310bd24e46476e46d6112ce66ff8e3e4b4","kind":"same-version-chinese-manual-with-authored-translation-of-reviewed-guidance","manual":{"anchor":"USING-EXPLAIN-CAVEATS","build_id":107,"extraction":"concept-introduction","fragment_sha256":"1726362ef840934bdec0f9ec72c682b2350a203639c9fd1e22dc5d29ac24ca1f","locale":"zh-Hans","page_key":"using-explain.html","source_hash":"d2f749744b3ac5aa2eb0154f295e8163903cd20fcc130c74c6172cdf2ae3a9df","source_revision":"unknown:rendered-translation-baseline","translated_from_revision":null,"translation_revision":"git:b1b500221dbe5437cfca004d6b04d3f6e422765d;html-sha256:08e987a66af27b98444a8057c256314eafa13611a3fb9e888214fc436824244b","url":"/zh/docs/18/using-explain.html#USING-EXPLAIN-CAVEATS","version":"18"},"runtime_rerun":false,"source_facts_revision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f"}},"ManualEvidence":{"release":{"channel":"stable","label":"18.6","major":"18","ref":"PostgreSQL 18.6 source archive","revision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","source_snapshot_utc":"","source_url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},"sources":[{"archive_sha256":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","label":"src/backend/commands/explain.c:1406","line":1406,"path":"src/backend/commands/explain.c","sha256":"34c86d6070224a0e981efef51f79101d6d505e5874f1684ace183034bab14bb4","url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},{"archive_sha256":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","label":"src/backend/executor/execProcnode.c:181","line":181,"path":"src/backend/executor/execProcnode.c","sha256":"f8a06a3f539077249b20664b2812433db6d7bd12b2c0ca633525db43d06f112a","url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},{"archive_sha256":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","label":"src/backend/executor/nodeAppend.c","path":"src/backend/executor/nodeAppend.c","sha256":"bfe49b7e44ce4c31878392c3d84eb5c6fa97a5185a39aca44ff1914033a84ad3","url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},{"archive_sha256":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","label":"src/include/nodes/plannodes.h","path":"src/include/nodes/plannodes.h","sha256":"52422b327a8049fbbb20d8b96008a0fc0a6fafa60f7eff3c695d5b2e83830120","url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},{"label":"PostgreSQL 18.6 · using-explain","path":"using-explain.html","sha256":"60040c30180093418a0affe56dd27dff9df2504b705b38039589e458bf5c31ed","url":"/docs/18/using-explain.html#USING-EXPLAIN-CAVEATS"},{"label":"PostgreSQL 18.6 · parallel-plans","path":"parallel-plans.html","sha256":"62207d207bead82b01b59dc119c4f95856f08655cc11d4699a05a40867ed2070","url":"/docs/18/parallel-plans.html#PARALLEL-APPEND"},{"label":"PostgreSQL 18.6 · using-explain","path":"using-explain.html","sha256":"60040c30180093418a0affe56dd27dff9df2504b705b38039589e458bf5c31ed","url":"/docs/18/using-explain.html#USING-EXPLAIN-ANALYZE"}]},"MeasuredEvidence":{"runtime_verified":false}},"Text":{"Collection":"plan","Key":"append","SourceDatabase":"center","Version":"18","Locale":"en","Title":"Append","Summary":"Visits its child plans and combines their rows.","BodyHTML":"\u003cp\u003eVisits its child plans and combines their rows.\u003c/p\u003e","SourceRevision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","ContentHash":"f719129f126b0c3568abdf4b3bda058935cb08641100a609f245d5c25b480f5d","Payload":{"description":["Visits its child plans and combines their rows."],"related":[{"label":"EXPLAIN","url":"/wiki/sql/explain/?v=18"},{"label":"Using EXPLAIN","url":"/docs/18/using-explain.html"},{"label":"Parallel plans","url":"/docs/18/parallel-plans.html"},{"label":"enable_parallel_append","url":"/wiki/guc/enable_parallel_append/?v=18"},{"label":"enable_async_append","url":"/wiki/guc/enable_async_append/?v=18"}],"sections":[{"paragraphs":["Structured formats use the Node Type above. Text-format spellings can also include operation, strategy, join type, scan direction or aggregation-stage attributes.","Text names recorded by this source: Append.","Parallel-aware and parallel-safe are different plan properties. A node running inside a parallel worker is not necessarily a parallel-aware node."],"title":"EXPLAIN names and attributes"},{"paragraphs":["This extraction does not assign a universal memory limit or spill policy to this node. Inspect the same-build implementation and its expressions or provider."],"title":"Memory and temporary storage"},{"paragraphs":["The source callbacks below can coordinate execution or collect worker instrumentation. Their presence is not a blanket claim that this node supports a shared parallel scan or shared state.","Callbacks in this build: ExecAppendEstimate, ExecAppendInitializeDSM, ExecAppendInitializeWorker, ExecAppendReInitializeDSM."],"title":"Parallel execution and instrumentation"},{"paragraphs":["Normally, EXPLAIN will display every plan node created by the planner. However, there are cases where the executor can determine that certain nodes need not be executed because they cannot produce any rows, based on parameter values that were not available at planning time. (Currently this can only happen for child nodes of an Append or MergeAppend node that is scanning a partitioned table.) When this happens, those plan nodes are omitted from the EXPLAIN output and a Subplans Removed: N annotation appears instead.","Whenever PostgreSQL needs to combine rows from multiple sources into a single result set, it uses an Append or MergeAppend plan node. This commonly happens when implementing UNION ALL or when scanning a partitioned table. Such nodes can be used in parallel plans just as they can in any other plan. However, in a parallel plan, the planner may instead use a Parallel Append node.","When an Append node is used in a parallel plan, each process will execute the child plans in the order in which they appear, so that all participating processes cooperate to execute the first child plan until it is complete and then move to the second plan at around the same time. When a Parallel Append is used instead, the executor will instead spread out the participating processes as evenly as possible across its child plans, so that multiple child plans are executed simultaneously. This avoids contention, and also avoids paying the startup cost of a child plan in those processes that never execute it.","Also, unlike a regular Append node, which can only have partial children when used within a parallel plan, a Parallel Append node can have both partial and non-partial child plans. Non-partial children will be scanned by only a single process, since scanning them more than once would produce duplicate results. Plans that involve appending multiple result sets can therefore achieve coarse-grained parallelism even when efficient partial plans are not available. For example, consider a query against a partitioned table that can only be implemented efficiently by using an index that does not support parallel scans. The planner might choose a Parallel Append of regular Index Scan plans; each individual index scan would have to be executed to completion by a single process, but different scans could be performed at the same time by different processes."],"title":"Same-version manual discussion"},{"blocks":[{"code":"EXPLAIN UPDATE gtest_parent SET f1 = CURRENT_DATE WHERE f2 = 101;\n\n                                       QUERY PLAN\n----------------------------------------------------------------------------------------\n Update on gtest_parent  (cost=0.00..3.06 rows=0 width=0)\n   Update on gtest_child gtest_parent_1\n   Update on gtest_child2 gtest_parent_2\n   Update on gtest_child3 gtest_parent_3\n   -\u003e  Append  (cost=0.00..3.06 rows=3 width=14)\n         -\u003e  Seq Scan on gtest_child gtest_parent_1  (cost=0.00..1.01 rows=1 width=14)\n               Filter: (f2 = 101)\n         -\u003e  Seq Scan on gtest_child2 gtest_parent_2  (cost=0.00..1.01 rows=1 width=14)\n               Filter: (f2 = 101)\n         -\u003e  Seq Scan on gtest_child3 gtest_parent_3  (cost=0.00..1.01 rows=1 width=14)\n               Filter: (f2 = 101)","paragraphs":["Example copied from the PostgreSQL 18.6 manual; it was not executed for this collection.","When an UPDATE , DELETE , or MERGE command affects a partitioned table or inheritance hierarchy, the output might look like this:"],"source":{"label":"PostgreSQL 18.6 · using-explain","path":"using-explain.html","sha256":"60040c30180093418a0affe56dd27dff9df2504b705b38039589e458bf5c31ed","url":"/docs/18/using-explain.html#USING-EXPLAIN-ANALYZE"}}],"title":"Examples from this manual build"},{"paragraphs":["NOTES Each append node contains a list of one or more subplans which must be iteratively processed (forwards or backwards). Tuples are retrieved by executing the 'whichplan'th subplan until the subplan stops returning tuples, at which point that plan is shut down and the next started up.","Append nodes don't make use of their left and right subtrees, rather they maintain a list of subplans so a typical append node looks like this in the plan tree:","... / Append -------+------+------+--- nil / \\ | | | nil nil ... ... ... subplans","Append nodes are currently used for unions, and to support inheritance queries, where several relations need to be scanned. For example, in our standard person/student/employee/student-emp example, where student and employee inherit from person and student-emp inherits from student and employee, the query:","| Append -------+-------+--------+--------+ / \\ | | | | nil nil Scan Scan Scan Scan | | | | person employee student student-emp"],"title":"Executor implementation notes"},{"code":"case T_Append:\n\t\t\tpname = sname = \"Append\";\n\t\t\tbreak;","title":"EXPLAIN identity in core source"}],"tables":[{"columns":[{"key":"label","label":"Text-format label"},{"key":"identity","label":"Structured node identity"}],"key":"explain-labels","rows":[{"identity":"Append","label":"Append"}],"title":"EXPLAIN labels in this source build"}]}},"RequestedLocale":"en","Fallback":false,"Versions":["10","11","12","13","14","15","16","17","18","19","20"],"Locales":["en","zh-Hans"],"Signatures":null,"Spellings":null,"SQLState":null,"Evidence":null}
