Unique
Unique
从已排序输入流中移除相邻重复项。
当前查看 PostgreSQL 18.6。
说明
从已排序输入流中移除相邻重复项。
- 核心节点标签
- T_Unique
- 结构化 EXPLAIN 节点类型
- Unique
- 输入
- 一个已排序的子计划
- 输出
- 去重后的元组
- 执行器初始化函数
- ExecInitUnique
- 内存机制
- unclassified
EXPLAIN 名称与属性
结构化格式使用上述 Node Type。文本格式名称还可能包含操作、策略、连接类型、扫描方向或聚合阶段属性。
此源码记录的文本名称:Unique.
并行感知与并行安全是不同的计划属性。在并行工作进程内运行的节点不一定是并行感知节点。
内存与临时存储
本次抽取不为此节点设定统一的内存上限或落盘策略。请查看同一构建的实现、相关表达式或提供方。
并行执行与运行信息采集
以下源码回调可以协调执行或收集工作进程的测量数据。回调存在不代表该节点普遍支持共享并行扫描或共享状态。
此构建的回调:none extracted from this node implementation.
同版本手册说明
新增的条件 stringu1 = 'xxx' 降低了估计输出行数,但没有降低代价,因为仍然需要访问同一批行。这是因为 stringu1 子句不能作为索引条件使用,毕竟这个索引只建立在 unique1 列上。它只能作为对通过索引取回行的过滤条件。因此,为了反映这项额外检查,代价实际上略微上升了一点。
此类计划按索引顺序获取表行,这使读取代价更高;但行数很少,不值得为行位置排序付出额外代价。查询只获取一行时,最常见到这种计划。如果查询的 ORDER BY 条件与索引顺序一致,也经常采用此计划,因为无需额外排序即可满足 ORDER BY。本例即使加上 ORDER BY unique1,仍会使用同一计划,因为索引已经隐式提供了所需顺序。
在这个计划中,有一个嵌套循环连接节点,它的两个输入,也就是两个子节点,都是表扫描。节点摘要行的缩进反映了计划树结构。连接的第一个子节点,也就是 “ 外侧 ” 子节点,是一个与前面见过的位图扫描类似的节点。它的代价和行计数与 SELECT ... WHERE unique1 < 10 得到的结果相同,因为 WHERE 子句 unique1 < 10 正是在该节点上应用的。 t1.unique2 = t2.unique2 子句此时还无关,因此不会影响外侧扫描的行计数。嵌套循环连接节点会对从外侧子节点得到的每一行执行一次第二个,也就是 “ 内侧 ” 子节点。当前外侧行中的列值可以代入内侧扫描;这里外侧行的 t1.unique2 值可用,因此得到的计划和代价与前面看到的简单 SELECT ... WHERE t2.unique2 = constant 情形类似。(由于预期在对 t2 反复执行索引扫描期间会发生缓存命中,估计代价实际上比前面看到的略低一些。)随后,循环节点的代价建立在外侧扫描代价之上,再加上每个外侧行都要执行一次内侧扫描的代价(这里是 10 * 7.90),以及少量连接处理的 CPU 时间。
无法在 tenk2_unique2 索引中检查 t1.hundred < t2.hundred 条件,因此在连接节点应用它。这会降低连接节点的预计输出行数,但不改变两侧输入扫描。
这里看到一个使用 tenk1_four_unique1_idx 的 Index-Only Scan 节点,该索引是 tenk1 表上由 four 和 unique1 两列组成的多列索引。该扫描执行了 3 次搜索,每次都只读取一个索引叶子页面: “ four = 1 AND unique1 = 42 ” 、 “ four = 2 AND unique1 = 42 ” 以及 “ four = 3 AND unique1 = 42 ” 。正如 第 11.3 节 中讨论的那样,这个索引通常很适合作为跳过扫描的目标,因为它的前导列,也就是 four 列,只包含 4 个非重复值,而它的第二列也是最后一列,即 unique1 列,则包含大量非重复值。
本版手册中的示例
示例摘自 PostgreSQL 18.6 手册;本百科未实际执行此示例。
现在修改查询,添加 WHERE 条件:
EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 7000;
QUERY PLAN
------------------------------------------------------------
Seq Scan on tenk1 (cost=0.00..470.00 rows=7000 width=244)
Filter: (unique1 < 7000)示例摘自 PostgreSQL 18.6 手册;本百科未实际执行此示例。
现在进一步收紧条件:
EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100;
QUERY PLAN
------------------------------------------------------------------------------
Bitmap Heap Scan on tenk1 (cost=5.06..224.98 rows=100 width=244)
Recheck Cond: (unique1 < 100)
-> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=100 width=0)
Index Cond: (unique1 < 100)执行器实现说明
nodeUnique.c:在适当情况下对查询去重的例程。
Unique 是一个非常简单的节点,从子计划返回的已排序元组流中过滤重复元组。它基本上是简化的 Group:去重功能相同,但不执行投影或条件检查,因此在两者都不需要时效率略高。(不过,为这点节省维护两种节点类型是否值得,仍有争议。)
说明:假定子计划返回的元组已经排序。
现在循环读取,仅返回不重复的元组。假定输入已排序,因此可以轻易检测重复,并返回每个组的第一个元组。
否则,检查新元组是否与上次返回的元组相同。如果相同,则返回循环开头,从子计划再获取一个新元组。
核心源码中的 EXPLAIN 标识
case T_Unique:
pname = sname = "Unique";
break;本构建中的 EXPLAIN 标签
| 文本格式标签 | 结构化节点标识 |
|---|---|
| Unique | Unique |
相关条目
文档与源码
- src/backend/commands/explain.c:1577
- src/backend/executor/execProcnode.c:350
- src/backend/executor/nodeUnique.c
- src/include/nodes/plannodes.h
- PostgreSQL 18.6 · using-explain
- PostgreSQL 18.6 · using-explain
来源构建
- 版本
- 18.6
- 构建
- PostgreSQL 18.6 source archive
- 来源指纹
555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f
版本比较
PostgreSQL 17 → 18: 无变化。
比较已记录的接口与属性,排除来源指纹和构建元数据。某个样本中没有记录,不能据此判断实际引入或移除的版本。
相关条目
AppendAppendMerge AppendMergeAppendRecursive UnionRecursiveUnionSetOpSetOp
导出 JSON · 返回执行计划节点 · 收录范围为 PostgreSQL 10 至 20;最早采样版本不一定是实际引入版本。