{"Entry":{"collection":"plan","key":"aggregate","name":"Aggregate","aliases":["Agg","Aggregate","GroupAggregate","HashAggregate","MixedAggregate","T_Agg"],"metadata":{"aliases":["Agg","Aggregate","GroupAggregate","HashAggregate","MixedAggregate","T_Agg"],"category":"Aggregation","content_hash":"3730c8cba1f67b3d1d5027d1a7aeca45790a785dcb981566c0515edfad69e31f","derived_content_hash":"a066494f0fc8cecd2a153a3f510400c8ad524f8b27a03f0c3ee821c6e62c0275","imported_at":"2026-09-30T00:40:43.991193+08:00","name":"Aggregate","name_zh":"Agg","slug":"aggregate","summary":"Computes aggregate results using the selected grouping strategy and aggregation stage."}},"Definition":{"Collection":"plan","Key":"aggregate","SourceDatabase":"center","Version":"18","SourceTable":"plan_node","SourceKey":"aggregate","SourceRevision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","Facts":{"comparison_data":{"initializer":"ExecInitAgg","memory_mechanism":"aggregate-spill","node_tag":"T_Agg","parallel_callbacks":["ExecAggEstimate","ExecAggInitializeDSM","ExecAggInitializeWorker","ExecAggRetrieveInstrumentation"],"partial_modes":["Partial","Finalize","Simple"],"strategies":["Plain","Sorted","Hashed","Mixed"],"text_names":["Aggregate","GroupAggregate","HashAggregate","MixedAggregate"]},"comparison_hash":"1144b010ce8f8de60989a1cd00684cccaefed0d3e7341c71e2fbf2edd3e504eb","description":["Computes aggregate results using the selected grouping strategy and aggregation stage."],"description_zh":"按选定的分组策略和聚合阶段计算聚合结果。","description_zh_html":"\u003cp\u003e按选定的分组策略和聚合阶段计算聚合结果。\u003c/p\u003e\u003cp\u003e核心节点标识：\u003ccode\u003eT_Agg\u003c/code\u003e；EXPLAIN 名称：\u003ccode\u003eAggregate, GroupAggregate, HashAggregate, MixedAggregate\u003c/code\u003e。\u003c/p\u003e\u003cp\u003e本版本源码中的并行执行或观测回调：\u003ccode\u003eExecAggEstimate, ExecAggInitializeDSM, ExecAggInitializeWorker, ExecAggRetrieveInstrumentation\u003c/code\u003e。回调的存在不表示该节点必然支持共享并行扫描；在并行工作进程中执行也不等于具有并行感知能力。\u003c/p\u003e\u003ch3\u003e同版本中文手册节选\u003c/h3\u003e\u003cp\u003e\n\u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 通过分两个阶段进行聚合来支持并行聚合。首先，每个参与查询并行部分的进程执行一个聚合步骤，为该进程所见到的每个分组产生一个部分结果。这在计划中体现为一个 \u003ccode class=\"literal\"\u003ePartial Aggregate\u003c/code\u003e 节点。然后，部分结果通过 \u003ccode class=\"literal\"\u003eGather\u003c/code\u003e 或 \u003ccode class=\"literal\"\u003eGather Merge\u003c/code\u003e 传送给领导者。最后，领导者会把来自所有工作进程的结果再次聚合，以产生最终结果。这在计划中体现为一个 \u003ccode class=\"literal\"\u003eFinalize Aggregate\u003c/code\u003e 节点。\n  \u003c/p\u003e\u003cp\u003e\n    由于 \u003ccode class=\"literal\"\u003eFinalize Aggregate\u003c/code\u003e 节点运行在领导者进程上，因此对于那些相对于输入行数会产生较多分组的查询，查询规划器会认为它不太有利。例如，在最坏情况下，\u003ccode class=\"literal\"\u003eFinalize Aggregate\u003c/code\u003e 节点看到的分组数可能与所有工作进程在 \u003ccode class=\"literal\"\u003ePartial Aggregate\u003c/code\u003e 阶段看到的输入行数一样多。对于这种情况，使用并行聚合显然不会带来性能收益。查询规划器会在规划过程中考虑这一点，因此在这种场景下不太可能选择并行聚合。\n  \u003c/p\u003e\u003cp\u003e\n    并行聚合并非在所有情况下都受支持。每个聚合都必须是\u003ca class=\"link\" href=\"/zh/docs/18/parallel-safety.html\"\u003e并行安全\u003c/a\u003e的，并且必须具有合并函数。如果该聚合具有类型为 \u003ccode class=\"literal\"\u003einternal\u003c/code\u003e 的转移状态，那么它还必须具有序列化和反序列化函数。更多细节请参见 \u003ca class=\"xref\" href=\"/zh/docs/18/sql-createaggregate.html\"\u003e\u003cspan class=\"refentrytitle\"\u003eCREATE AGGREGATE\u003c/span\u003e\u003c/a\u003e。如果任何聚合函数调用包含 \u003ccode class=\"literal\"\u003eDISTINCT\u003c/code\u003e 或 \u003ccode class=\"literal\"\u003eORDER BY\u003c/code\u003e 子句，则不支持并行聚合。对于有序集聚合，或者当查询涉及 \u003ccode class=\"literal\"\u003eGROUPING SETS\u003c/code\u003e 时，也不支持并行聚合。只有当查询涉及的所有连接也都属于计划并行部分时，才能使用并行聚合。\n  \u003c/p\u003e\u003cp\u003e\u003ca href=\"/zh/docs/18/parallel-plans.html#PARALLEL-AGGREGATION\"\u003e查看完整中文手册与上下文\u003c/a\u003e。\u003c/p\u003e\u003cp\u003e本页开头的中文说明为 PG.CENTER 对编辑说明的翻译；手册节选保留对应版本中文文档原文。源码信息及手册示例不代表重新进行过运行测试。\u003c/p\u003e","evidence_kind":"source and documentation","explain_names":["Aggregate","GroupAggregate","HashAggregate","MixedAggregate"],"explain_prefixes":["Parallel","Async"],"facts":[{"label":"Core node tag","value":"T_Agg"},{"label":"Structured EXPLAIN Node Type","value":"Aggregate"},{"label":"Inputs","value":"One child plan"},{"label":"Output","value":"Aggregate result tuples"},{"label":"Executor initializer","value":"ExecInitAgg"},{"label":"Memory mechanism","value":"aggregate-spill"},{"label":"EXPLAIN strategies","value":"Plain, Sorted, Hashed, Mixed"},{"label":"Aggregation stages","value":"Partial, Finalize, Simple"}],"facts_zh":[{"label":"核心节点标签","value":"T_Agg"},{"label":"结构化 EXPLAIN 节点类型","value":"Aggregate"},{"label":"输入","value":"一个子计划"},{"label":"输出","value":"聚合结果元组"},{"label":"执行器初始化函数","value":"ExecInitAgg"},{"label":"内存机制","value":"aggregate-spill"},{"label":"EXPLAIN 策略","value":"Plain, Sorted, Hashed, Mixed"},{"label":"聚合阶段","value":"Partial, Finalize, Simple"}],"memory":{"description":"This implementation contains a disk-spill path for hashed aggregation. Aggregate input sorting and transition-state allocations have their own behavior; one spill policy does not describe every aggregation strategy.","evidence":[{"archive_sha256":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","label":"src/backend/executor/nodeAgg.c","path":"src/backend/executor/nodeAgg.c","sha256":"8719569a73f054e9026c0a00c814f60b6c7879753c58e4e6dcc38fee333c45c5","url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"}],"mechanism":"aggregate-spill","source_notes":["When performing hash aggregation, if the hash table memory exceeds the limit (see hash_agg_check_limits()), we enter \"spill mode\". In spill mode, we advance the transition states only for groups already in the hash table. For tuples that would need to create a new hash table entries (and initialize new transition states), we instead spill them to disk to be processed later. The tuples are spilled in a partitioned manner, so that subsequent batches are smaller and less likely to exceed hash_mem (if a batch does exceed hash_mem, it must be spilled recursively).","Spilled data is written to logical tapes. These provide better control over memory usage, disk space, and the number of files than if we were to use a BufFile for each spill. We don't know the number of tapes needed at the start of the algorithm (because it can recurse), so a tape set is allocated at the beginning, and individual tapes are created as needed. As a particular tape is read, logtape.c recycles its disk space. When a tape is read to completion, it is destroyed entirely.","Control how many partitions are created when spilling HashAgg to disk.","We also specify a min and max number of partitions per spill. Too few might mean a lot of wasted I/O from repeated spilling of the same tuples. Too many will result in lots of memory wasted buffering the spill files (which could instead be spent on a larger hash table)."]},"node_tag":"T_Agg","parallel_callbacks":["ExecAggEstimate","ExecAggInitializeDSM","ExecAggInitializeWorker","ExecAggRetrieveInstrumentation"],"partial_modes":["Partial","Finalize","Simple"],"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_hashagg","url":"/wiki/guc/enable_hashagg/?v=18"},{"label":"work_mem","url":"/wiki/guc/work_mem/?v=18"},{"label":"hash_mem_multiplier","url":"/wiki/guc/hash_mem_multiplier/?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: Aggregate, GroupAggregate, HashAggregate, MixedAggregate.","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 implementation contains a disk-spill path for hashed aggregation. Aggregate input sorting and transition-state allocations have their own behavior; one spill policy does not describe every aggregation strategy.","When performing hash aggregation, if the hash table memory exceeds the limit (see hash_agg_check_limits()), we enter \"spill mode\". In spill mode, we advance the transition states only for groups already in the hash table. For tuples that would need to create a new hash table entries (and initialize new transition states), we instead spill them to disk to be processed later. The tuples are spilled in a partitioned manner, so that subsequent batches are smaller and less likely to exceed hash_mem (if a batch does exceed hash_mem, it must be spilled recursively).","Spilled data is written to logical tapes. These provide better control over memory usage, disk space, and the number of files than if we were to use a BufFile for each spill. We don't know the number of tapes needed at the start of the algorithm (because it can recurse), so a tape set is allocated at the beginning, and individual tapes are created as needed. As a particular tape is read, logtape.c recycles its disk space. When a tape is read to completion, it is destroyed entirely.","Control how many partitions are created when spilling HashAgg to disk.","We also specify a min and max number of partitions per spill. Too few might mean a lot of wasted I/O from repeated spilling of the same tuples. Too many will result in lots of memory wasted buffering the spill files (which could instead be spent on a larger hash table)."],"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: ExecAggEstimate, ExecAggInitializeDSM, ExecAggInitializeWorker, ExecAggRetrieveInstrumentation."],"title":"Parallel execution and instrumentation"},{"paragraphs":["PostgreSQL supports parallel aggregation by aggregating in two stages. First, each process participating in the parallel portion of the query performs an aggregation step, producing a partial result for each group of which that process is aware. This is reflected in the plan as a Partial Aggregate node. Second, the partial results are transferred to the leader via Gather or Gather Merge . Finally, the leader re-aggregates the results across all workers in order to produce the final result. This is reflected in the plan as a Finalize Aggregate node.","Because the Finalize Aggregate node runs on the leader process, queries that produce a relatively large number of groups in comparison to the number of input rows will appear less favorable to the query planner. For example, in the worst-case scenario the number of groups seen by the Finalize Aggregate node could be as many as the number of input rows that were seen by all worker processes in the Partial Aggregate stage. For such cases, there is clearly going to be no performance benefit to using parallel aggregation. The query planner takes this into account during the planning process and is unlikely to choose parallel aggregate in this scenario.","Parallel aggregation is not supported in all situations. Each aggregate must be safe for parallelism and must have a combine function. If the aggregate has a transition state of type internal , it must have serialization and deserialization functions. See CREATE AGGREGATE for more details. Parallel aggregation is not supported if any aggregate function call contains DISTINCT or ORDER BY clause and is also not supported for ordered set aggregates or when the query involves GROUPING SETS . It can only be used when all joins involved in the query are also part of the parallel portion of the plan."],"title":"Same-version manual discussion"},{"paragraphs":["ExecAgg normally evaluates each aggregate in the following steps:","transvalue = initcond foreach input_tuple do transvalue = transfunc(transvalue, input_value(s)) result = finalfunc(transvalue, direct_argument(s))","If a finalfunc is not supplied then the result is just the ending value of transvalue.","Other behaviors can be selected by the \"aggsplit\" mode, which exists to support partial aggregation. It is possible to: * Skip running the finalfunc, so that the output is always the final transvalue state. * Substitute the combinefunc for the transfunc, so that transvalue states (propagated up from a child partial-aggregation step) are merged rather than processing raw input rows. (The statements below about the transfunc apply equally to the combinefunc, when it's selected.) * Apply the serializefunc to the output values (this only makes sense when skipping the finalfunc, since the serializefunc works on the transvalue data type). * Apply the deserializefunc to the input values (this only makes sense when using the combinefunc, for similar reasons). It is the planner's responsibility to connect up Agg nodes using these alternate behaviors in a way that makes sense, with partial aggregation results being fed to nodes that expect them.","If a normal aggregate call specifies DISTINCT or ORDER BY, we sort the input tuples and eliminate duplicates (if required) before performing the above-depicted process. (However, we don't do that for ordered-set aggregates; their \"ORDER BY\" inputs are ordinary aggregate arguments so far as this module is concerned.) Note that partial aggregation is not supported in these cases, since we couldn't ensure global ordering or distinctness of the inputs."],"title":"Executor implementation notes"},{"code":"case T_Agg:\n\t\t\t{\n\t\t\t\tAgg\t\t   *agg = (Agg *) plan;\n\n\t\t\t\tsname = \"Aggregate\";\n\t\t\t\tswitch (agg-\u003eaggstrategy)\n\t\t\t\t{\n\t\t\t\t\tcase AGG_PLAIN:\n\t\t\t\t\t\tpname = \"Aggregate\";\n\t\t\t\t\t\tstrategy = \"Plain\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase AGG_SORTED:\n\t\t\t\t\t\tpname = \"GroupAggregate\";\n\t\t\t\t\t\tstrategy = \"Sorted\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase AGG_HASHED:\n\t\t\t\t\t\tpname = \"HashAggregate\";\n\t\t\t\t\t\tstrategy = \"Hashed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase AGG_MIXED:\n\t\t\t\t\t\tpname = \"MixedAggregate\";\n\t\t\t\t\t\tstrategy = \"Mixed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tpname = \"Aggregate ???\";\n\t\t\t\t\t\tstrategy = \"???\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (DO_AGGSPLIT_SKIPFINAL(agg-\u003eaggsplit))\n\t\t\t\t{\n\t\t\t\t\tpartialmode = \"Partial\";\n\t\t\t\t\tpname = psprintf(\"%s %s\", partialmode, pname);\n\t\t\t\t}\n\t\t\t\telse if (DO_AGGSPLIT_COMBINE(agg-\u003eaggsplit))\n\t\t\t\t{\n\t\t\t\t\tpartialmode = \"Finalize\";\n\t\t\t\t\tpname = psprintf(\"%s %s\", partialmode, pname);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tpartialmode = \"Simple\";\n\t\t\t}\n\t\t\tbreak;","title":"EXPLAIN identity in core source"}],"sections_zh":[{"paragraphs":["结构化格式使用上述 Node Type。文本格式名称还可能包含操作、策略、连接类型、扫描方向或聚合阶段属性。","此源码记录的文本名称：Aggregate, GroupAggregate, HashAggregate, MixedAggregate.","并行感知与并行安全是不同的计划属性。在并行工作进程内运行的节点不一定是并行感知节点。"],"title":"EXPLAIN 名称与属性"},{"paragraphs":["此实现包含哈希聚合的落盘路径。聚合输入排序和转换状态分配各有行为，单一落盘策略不能描述所有聚合策略。","执行哈希聚合时，如果哈希表内存超过限制（见 hash_agg_check_limits()），就进入落盘模式。此模式下，只为哈希表中已有的分组推进转换状态。对于需要新建哈希条目并初始化新转换状态的元组，改为写入磁盘，稍后处理。元组按分区方式落盘，使后续批次更小，更不容易超过 hash_mem；如果某批次仍超出 hash_mem，就必须递归落盘。","落盘数据写入逻辑磁带。与为每次落盘分别使用 BufFile 相比，这样能够更好地控制内存用量、磁盘空间和文件数量。由于算法可能递归，开始时不知道需要多少磁带，因此先分配磁带集合，再按需创建各条磁带。读取磁带时，logtape.c 回收其磁盘空间；读完后将整条磁带销毁。","控制 HashAgg 落盘时创建的分区数。","还会指定每次落盘的最小和最大分区数。分区太少可能反复将相同元组写入磁盘，浪费大量 I/O；分区太多则会用大量内存缓冲落盘文件，而这些内存原本可以用于更大的哈希表。"],"title":"内存与临时存储"},{"paragraphs":["以下源码回调可以协调执行或收集工作进程的测量数据。回调存在不代表该节点普遍支持共享并行扫描或共享状态。","此构建的回调：ExecAggEstimate, ExecAggInitializeDSM, ExecAggInitializeWorker, ExecAggRetrieveInstrumentation."],"title":"并行执行与运行信息采集"},{"paragraphs":["PostgreSQL 通过分两个阶段进行聚合来支持并行聚合。首先，每个参与查询并行部分的进程执行一个聚合步骤，为该进程所见到的每个分组产生一个部分结果。这在计划中体现为一个 Partial Aggregate 节点。然后，部分结果通过 Gather 或 Gather Merge 传送给领导者。最后，领导者会把来自所有工作进程的结果再次聚合，以产生最终结果。这在计划中体现为一个 Finalize Aggregate 节点。","由于 Finalize Aggregate 节点运行在领导者进程上，因此对于那些相对于输入行数会产生较多分组的查询，查询规划器会认为它不太有利。例如，在最坏情况下， Finalize Aggregate 节点看到的分组数可能与所有工作进程在 Partial Aggregate 阶段看到的输入行数一样多。对于这种情况，使用并行聚合显然不会带来性能收益。查询规划器会在规划过程中考虑这一点，因此在这种场景下不太可能选择并行聚合。","并行聚合并非在所有情况下都受支持。每个聚合都必须是 并行安全 的，并且必须具有合并函数。如果该聚合具有类型为 internal 的转移状态，那么它还必须具有序列化和反序列化函数。更多细节请参见 CREATE AGGREGATE 。如果任何聚合函数调用包含 DISTINCT 或 ORDER BY 子句，则不支持并行聚合。对于有序集聚合，或者当查询涉及 GROUPING SETS 时，也不支持并行聚合。只有当查询涉及的所有连接也都属于计划并行部分时，才能使用并行聚合。"],"title":"同版本手册说明"},{"paragraphs":["ExecAgg 通常按以下步骤求值每个聚合：","transvalue = initcond foreach input_tuple do transvalue = transfunc(transvalue, input_value(s)) result = finalfunc(transvalue, direct_argument(s))","如果未提供 finalfunc，结果就是 transvalue 的最终值。","aggsplit 模式用于支持部分聚合，并可选择其他行为：跳过 finalfunc，使输出始终为最终 transvalue 状态；用 combinefunc 替代 transfunc，以合并从子级部分聚合传来的 transvalue 状态，而非处理原始输入行（选用 combinefunc 时，下文关于 transfunc 的说明同样适用）；对输出应用 serializefunc（仅在跳过 finalfunc 时有意义，因为 serializefunc 处理 transvalue 类型）；对输入应用 deserializefunc（同理，仅在使用 combinefunc 时有意义）。规划器负责正确连接采用这些行为的 Agg 节点，把部分聚合结果交给预期接收它们的节点。","如果普通聚合调用指定了 DISTINCT 或 ORDER BY，我们会先对输入元组排序，并在需要时去重，再执行上文所述的过程。（但有序集聚合不这样处理；对于本模块而言，其 ORDER BY 输入只是普通聚合参数。）这些情况下不支持部分聚合，因为无法保证输入在全局范围内有序或唯一。"],"title":"执行器实现说明"},{"code":"case T_Agg:\n\t\t\t{\n\t\t\t\tAgg\t\t   *agg = (Agg *) plan;\n\n\t\t\t\tsname = \"Aggregate\";\n\t\t\t\tswitch (agg-\u003eaggstrategy)\n\t\t\t\t{\n\t\t\t\t\tcase AGG_PLAIN:\n\t\t\t\t\t\tpname = \"Aggregate\";\n\t\t\t\t\t\tstrategy = \"Plain\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase AGG_SORTED:\n\t\t\t\t\t\tpname = \"GroupAggregate\";\n\t\t\t\t\t\tstrategy = \"Sorted\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase AGG_HASHED:\n\t\t\t\t\t\tpname = \"HashAggregate\";\n\t\t\t\t\t\tstrategy = \"Hashed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase AGG_MIXED:\n\t\t\t\t\t\tpname = \"MixedAggregate\";\n\t\t\t\t\t\tstrategy = \"Mixed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tpname = \"Aggregate ???\";\n\t\t\t\t\t\tstrategy = \"???\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (DO_AGGSPLIT_SKIPFINAL(agg-\u003eaggsplit))\n\t\t\t\t{\n\t\t\t\t\tpartialmode = \"Partial\";\n\t\t\t\t\tpname = psprintf(\"%s %s\", partialmode, pname);\n\t\t\t\t}\n\t\t\t\telse if (DO_AGGSPLIT_COMBINE(agg-\u003eaggsplit))\n\t\t\t\t{\n\t\t\t\t\tpartialmode = \"Finalize\";\n\t\t\t\t\tpname = psprintf(\"%s %s\", partialmode, pname);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tpartialmode = \"Simple\";\n\t\t\t}\n\t\t\tbreak;","title":"核心源码中的 EXPLAIN 标识"}],"source_inventory":{"executor":"src/backend/executor/execProcnode.c","explain":"src/backend/commands/explain.c","implementation":"src/backend/executor/nodeAgg.c"},"sources":[{"archive_sha256":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","label":"src/backend/commands/explain.c:1531","line":1531,"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:340","line":340,"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/nodeAgg.c","path":"src/backend/executor/nodeAgg.c","sha256":"8719569a73f054e9026c0a00c814f60b6c7879753c58e4e6dcc38fee333c45c5","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 · parallel-plans","path":"parallel-plans.html","sha256":"62207d207bead82b01b59dc119c4f95856f08655cc11d4699a05a40867ed2070","url":"/docs/18/parallel-plans.html#PARALLEL-AGGREGATION"}],"strategies":["Plain","Sorted","Hashed","Mixed"],"tables":[{"columns":[{"key":"label","label":"Text-format label"},{"key":"identity","label":"Structured node identity"}],"key":"explain-labels","rows":[{"identity":"Aggregate","label":"Aggregate"},{"identity":"Aggregate","label":"GroupAggregate"},{"identity":"Aggregate","label":"HashAggregate"},{"identity":"Aggregate","label":"MixedAggregate"}],"title":"EXPLAIN labels in this source build"}],"tables_zh":[{"columns":[{"key":"label","label":"文本格式标签"},{"key":"identity","label":"结构化节点标识"}],"key":"explain-labels","rows":[{"identity":"Aggregate","label":"Aggregate"},{"identity":"Aggregate","label":"GroupAggregate"},{"identity":"Aggregate","label":"HashAggregate"},{"identity":"Aggregate","label":"MixedAggregate"}],"title":"本构建中的 EXPLAIN 标签"}],"zh_from":{"authored_translation":"authored-translation-of-reviewed-guidance","body_sha256":"2da48543f4470ebd87f3ba973d9ffaabfee1a75c1dfe1c56083e6e7895eec873","kind":"same-version-chinese-manual-with-authored-translation-of-reviewed-guidance","manual":{"anchor":"PARALLEL-AGGREGATION","build_id":107,"extraction":"concept-introduction","fragment_sha256":"f882e19de96b3173fd88cb5de956b736c655192b92b7cdbf544225b9bedc6662","locale":"zh-Hans","page_key":"parallel-plans.html","source_hash":"1ef9f9a3740440f8dcdf04c2a2dfe2f7d48c66f2e0b102d15df580bff22a6857","source_revision":"unknown:rendered-translation-baseline","translated_from_revision":null,"translation_revision":"git:b1b500221dbe5437cfca004d6b04d3f6e422765d;html-sha256:08e987a66af27b98444a8057c256314eafa13611a3fb9e888214fc436824244b","url":"/zh/docs/18/parallel-plans.html#PARALLEL-AGGREGATION","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:1531","line":1531,"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:340","line":340,"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/nodeAgg.c","path":"src/backend/executor/nodeAgg.c","sha256":"8719569a73f054e9026c0a00c814f60b6c7879753c58e4e6dcc38fee333c45c5","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 · parallel-plans","path":"parallel-plans.html","sha256":"62207d207bead82b01b59dc119c4f95856f08655cc11d4699a05a40867ed2070","url":"/docs/18/parallel-plans.html#PARALLEL-AGGREGATION"}]},"MeasuredEvidence":{"runtime_verified":false}},"Text":{"Collection":"plan","Key":"aggregate","SourceDatabase":"center","Version":"18","Locale":"en","Title":"Aggregate","Summary":"Computes aggregate results using the selected grouping strategy and aggregation stage.","BodyHTML":"\u003cp\u003eComputes aggregate results using the selected grouping strategy and aggregation stage.\u003c/p\u003e","SourceRevision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","ContentHash":"56e23a2d3c8c08bdc1293eb388d4589b28947d73eb7c4acee2b0e732e6d5b66c","Payload":{"description":["Computes aggregate results using the selected grouping strategy and aggregation stage."],"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_hashagg","url":"/wiki/guc/enable_hashagg/?v=18"},{"label":"work_mem","url":"/wiki/guc/work_mem/?v=18"},{"label":"hash_mem_multiplier","url":"/wiki/guc/hash_mem_multiplier/?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: Aggregate, GroupAggregate, HashAggregate, MixedAggregate.","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 implementation contains a disk-spill path for hashed aggregation. Aggregate input sorting and transition-state allocations have their own behavior; one spill policy does not describe every aggregation strategy.","When performing hash aggregation, if the hash table memory exceeds the limit (see hash_agg_check_limits()), we enter \"spill mode\". In spill mode, we advance the transition states only for groups already in the hash table. For tuples that would need to create a new hash table entries (and initialize new transition states), we instead spill them to disk to be processed later. The tuples are spilled in a partitioned manner, so that subsequent batches are smaller and less likely to exceed hash_mem (if a batch does exceed hash_mem, it must be spilled recursively).","Spilled data is written to logical tapes. These provide better control over memory usage, disk space, and the number of files than if we were to use a BufFile for each spill. We don't know the number of tapes needed at the start of the algorithm (because it can recurse), so a tape set is allocated at the beginning, and individual tapes are created as needed. As a particular tape is read, logtape.c recycles its disk space. When a tape is read to completion, it is destroyed entirely.","Control how many partitions are created when spilling HashAgg to disk.","We also specify a min and max number of partitions per spill. Too few might mean a lot of wasted I/O from repeated spilling of the same tuples. Too many will result in lots of memory wasted buffering the spill files (which could instead be spent on a larger hash table)."],"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: ExecAggEstimate, ExecAggInitializeDSM, ExecAggInitializeWorker, ExecAggRetrieveInstrumentation."],"title":"Parallel execution and instrumentation"},{"paragraphs":["PostgreSQL supports parallel aggregation by aggregating in two stages. First, each process participating in the parallel portion of the query performs an aggregation step, producing a partial result for each group of which that process is aware. This is reflected in the plan as a Partial Aggregate node. Second, the partial results are transferred to the leader via Gather or Gather Merge . Finally, the leader re-aggregates the results across all workers in order to produce the final result. This is reflected in the plan as a Finalize Aggregate node.","Because the Finalize Aggregate node runs on the leader process, queries that produce a relatively large number of groups in comparison to the number of input rows will appear less favorable to the query planner. For example, in the worst-case scenario the number of groups seen by the Finalize Aggregate node could be as many as the number of input rows that were seen by all worker processes in the Partial Aggregate stage. For such cases, there is clearly going to be no performance benefit to using parallel aggregation. The query planner takes this into account during the planning process and is unlikely to choose parallel aggregate in this scenario.","Parallel aggregation is not supported in all situations. Each aggregate must be safe for parallelism and must have a combine function. If the aggregate has a transition state of type internal , it must have serialization and deserialization functions. See CREATE AGGREGATE for more details. Parallel aggregation is not supported if any aggregate function call contains DISTINCT or ORDER BY clause and is also not supported for ordered set aggregates or when the query involves GROUPING SETS . It can only be used when all joins involved in the query are also part of the parallel portion of the plan."],"title":"Same-version manual discussion"},{"paragraphs":["ExecAgg normally evaluates each aggregate in the following steps:","transvalue = initcond foreach input_tuple do transvalue = transfunc(transvalue, input_value(s)) result = finalfunc(transvalue, direct_argument(s))","If a finalfunc is not supplied then the result is just the ending value of transvalue.","Other behaviors can be selected by the \"aggsplit\" mode, which exists to support partial aggregation. It is possible to: * Skip running the finalfunc, so that the output is always the final transvalue state. * Substitute the combinefunc for the transfunc, so that transvalue states (propagated up from a child partial-aggregation step) are merged rather than processing raw input rows. (The statements below about the transfunc apply equally to the combinefunc, when it's selected.) * Apply the serializefunc to the output values (this only makes sense when skipping the finalfunc, since the serializefunc works on the transvalue data type). * Apply the deserializefunc to the input values (this only makes sense when using the combinefunc, for similar reasons). It is the planner's responsibility to connect up Agg nodes using these alternate behaviors in a way that makes sense, with partial aggregation results being fed to nodes that expect them.","If a normal aggregate call specifies DISTINCT or ORDER BY, we sort the input tuples and eliminate duplicates (if required) before performing the above-depicted process. (However, we don't do that for ordered-set aggregates; their \"ORDER BY\" inputs are ordinary aggregate arguments so far as this module is concerned.) Note that partial aggregation is not supported in these cases, since we couldn't ensure global ordering or distinctness of the inputs."],"title":"Executor implementation notes"},{"code":"case T_Agg:\n\t\t\t{\n\t\t\t\tAgg\t\t   *agg = (Agg *) plan;\n\n\t\t\t\tsname = \"Aggregate\";\n\t\t\t\tswitch (agg-\u003eaggstrategy)\n\t\t\t\t{\n\t\t\t\t\tcase AGG_PLAIN:\n\t\t\t\t\t\tpname = \"Aggregate\";\n\t\t\t\t\t\tstrategy = \"Plain\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase AGG_SORTED:\n\t\t\t\t\t\tpname = \"GroupAggregate\";\n\t\t\t\t\t\tstrategy = \"Sorted\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase AGG_HASHED:\n\t\t\t\t\t\tpname = \"HashAggregate\";\n\t\t\t\t\t\tstrategy = \"Hashed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase AGG_MIXED:\n\t\t\t\t\t\tpname = \"MixedAggregate\";\n\t\t\t\t\t\tstrategy = \"Mixed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tpname = \"Aggregate ???\";\n\t\t\t\t\t\tstrategy = \"???\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (DO_AGGSPLIT_SKIPFINAL(agg-\u003eaggsplit))\n\t\t\t\t{\n\t\t\t\t\tpartialmode = \"Partial\";\n\t\t\t\t\tpname = psprintf(\"%s %s\", partialmode, pname);\n\t\t\t\t}\n\t\t\t\telse if (DO_AGGSPLIT_COMBINE(agg-\u003eaggsplit))\n\t\t\t\t{\n\t\t\t\t\tpartialmode = \"Finalize\";\n\t\t\t\t\tpname = psprintf(\"%s %s\", partialmode, pname);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tpartialmode = \"Simple\";\n\t\t\t}\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":"Aggregate","label":"Aggregate"},{"identity":"Aggregate","label":"GroupAggregate"},{"identity":"Aggregate","label":"HashAggregate"},{"identity":"Aggregate","label":"MixedAggregate"}],"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}
