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

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

百科 / 执行计划节点 / 组合

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 标签

文本格式标签结构化节点标识
UniqueUnique

相关条目

文档与源码

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

版本比较

PostgreSQL 17 → 18: 无变化。

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

相关条目

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