云数据库 PostgreSQL 子查询上拉优化

原生 PostgreSQL 的 pull_up_sublinks 只能上拉 ANY / EXISTS / NOT EXISTS 等有限形式的子查询;对于以下常。

相关产品
云数据库 PostgreSQL 产品页,含版本规格、价格与购买入口。

一、功能概述

·WHERE x = (SELECT agg(…) FROM … WHERE … = outer.col)(相关 EXPR 子查询)。

·WHERE (SELECT …) IN (…) 出现在 OR 分支中。

·WHERE outer.col NOT IN (SELECT …)(NOT IN → 反连接)。

·SELECT (SELECT agg(…) FROM … WHERE … = outer.col), … 目标列的相关聚合子查询。

主干只能生成 SubPlan/InitPlan,无法转成显式 JOIN,导致大表反复回扫。云数据库 PostgreSQL 提供 tencentdb_sublink 扩展,注册 pull_up_sublinks_hook,在保留主干兼容性的前提下把这些子查询实现路径转写为 JOIN / ANTI JOIN / UNION,让后续路径生成能真正利用 HashJoin、MergeJoin、并行等能力。

覆盖的转换矩阵:

·TargetList SubLink 上拉:SELECT 列表内的相关聚合子查询作为 EXPR 上拉的一部分被处理。

·OR clause SubLink 上拉:OR 分支内的可上拉 SubLink 改写为 LEFT JOIN + IS NOT NULL / equality。

·EXPR SubLink 上拉:相关等值 EXPR 子查询上拉为 INNER/LEFT JOIN(当前 Phase 5 仅覆盖 hasAggs 子路径)。

·NOT IN → ANTI JOIN:满足 NULL 安全条件时改写为反连接。

·OR-子查询 → UNION 重写(可选):多路 OR-of-subqueries 合成一个 UNION 子查询后 JOIN。

二、前提条件

PostgreSQL 18,版本 ≥ v18.4_r1.10。

三、使用限制

·CREATE EXTENSION tencentdb_sublink 只登记 catalog,不会加载动态库;必须配合 shared_preload_libraries = 'tencentdb_sublink' 或 LOAD 'tencentdb_sublink' 使用。

·该扩展只调整 planner 阶段的重写,不改变查询结果集。

四、创建插件

CREATE EXTENSION tencentdb_sublink;SELECT extname, extversion FROM pg_extension WHERE extname = 'tencentdb_sublink';

需要动态库实际加载。推荐加入 postgresql.conf:

shared_preload_libraries = '…,tencentdb_sublink'

或每个会话临时加载:

LOAD 'tencentdb_sublink';

五、参数说明

参数 类型 默认 说明
tencentdb_sublink.enable bool on 插件总开关。关闭则完全走原生 standard_pull_up_sublinks。
tencentdb_sublink.enable_or bool on OR 子句中的 SubLink 上拉。
tencentdb_sublink.enable_not_in bool on NOT IN (subquery) → ANTI JOIN。
tencentdb_sublink.enable_expr bool off 相关 EXPR SubLink 上拉(含聚合子路径)。
tencentdb_sublink.enable_targetlist bool off 目标列相关聚合子查询上拉。必须先打开 enable_expr 才生效,因为它作为 EXPR 上拉路径的辅助 helper 被调用。
tencentdb_sublink.enable_or_union bool off OR-of-subqueries 合成 UNION 子查询后再连接。

六、使用示例

准备演示表:

CREATE TABLE t1 (a int, b int, c text);CREATE TABLE t2 (a int, b int, c text);CREATE TABLE t3 (a int, b int, c text);INSERT INTO t1 SELECT i, i % 10, 'r'||i FROM generate_series(1,100) i;INSERT INTO t2 SELECT i, i % 10, 'r'||i FROM generate_series(1,50) i;INSERT INTO t3 SELECT i, i % 10, 'r'||i FROM generate_series(1,30) i;ANALYZE t1, t2, t3;

七、默认已生效

SHOW tencentdb_sublink.enable;

八、NOT IN → ANTI JOIN

注意:说明:

注意:前提:外侧列与子查询目标列在 catalog 里都必须是 NOT NULL。

先给 t1.a / t2.a 加上 NOT NULL 约束:

ALTER TABLE t1 ALTER COLUMN a SET NOT NULL;ALTER TABLE t2 ALTER COLUMN a SET NOT NULL;

SET tencentdb_sublink.enable = on;SET tencentdb_sublink.enable_not_in = on;postgres=> EXPLAIN (COSTS OFF)postgres-> SELECT * FROM t1 WHERE t1.a NOT IN (SELECT t2.a FROM t2); QUERY PLAN —————————- Hash Anti Join Hash Cond: (t1.a = t2.a) -> Seq Scan on t1 -> Hash -> Seq Scan on t2(5 rows)

原本主干为 hashed SubPlan,插件将其重写为 Anti Join / Hash Anti Join,可与 HashJoin 结合。

九、OR 子句中的子查询上拉

SET tencentdb_sublink.enable to on;SET tencentdb_sublink.enable_or = on;postgres=> EXPLAIN (COSTS OFF)postgres-> SELECT * FROM t1postgres-> WHERE t1.b = 3postgres-> OR EXISTS (SELECT 1 FROM t2 WHERE t2.a = t1.a); QUERY PLAN ———————————————- Hash Left Join Hash Cond: (t1.a = t2.a) Filter: ((t1.b = 3) OR (t2.a IS NOT NULL)) -> Seq Scan on t1 -> Hash -> HashAggregate Group Key: t2.a -> Seq Scan on t2(8 rows)

OR 分支中的 EXISTS 被改写成 LEFT JOIN + … IS NOT NULL 分支,避免整表回扫。

十、EXPR 相关子查询(含 TargetList 聚合子查询)

打开 enable_expr 后,WHERE 中的相关等值 EXPR 子查询可上拉:

SET tencentdb_sublink.enable_expr = on;postgres=> EXPLAIN (COSTS OFF)postgres-> SELECT * FROM t1postgres-> WHERE t1.b = (SELECT sum(t2.b) FROM t2 WHERE t2.a = t1.a); QUERY PLAN ——————————————————- Hash Join Hash Cond: ((t1.b = (sum(t2.b))) AND (t1.a = t2.a)) -> Seq Scan on t1 -> Hash -> HashAggregate Group Key: t2.a -> Seq Scan on t2(7 rows)

同时打开 enable_targetlist 时,SELECT 列表内的相关聚合子查询也进入同一路径:

SET tencentdb_sublink.enable_expr = on;SET tencentdb_sublink.enable_targetlist = on;postgres=> EXPLAIN (COSTS OFF)postgres-> SELECT t1.a,postgres-> (SELECT sum(t2.b) FROM t2 WHERE t2.a = t1.a) AS spostgres-> FROM t1; QUERY PLAN ———————————- Hash Left Join Hash Cond: (t1.a = t2.a) -> Seq Scan on t1 -> Hash -> HashAggregate Group Key: t2.a -> Seq Scan on t2(7 rows)

注意:说明:

注意:EXPR / TargetList 目前仅覆盖 hasAggs 子路径。

十一、OR-子查询 → UNION 重写

SET tencentdb_sublink.enable_or_union = on;postgres=> EXPLAIN (COSTS OFF)postgres-> SELECT * FROM t1postgres-> WHERE t1.a IN (SELECT t2.a FROM t2 WHERE t2.b = 1)postgres-> OR t1.a IN (SELECT t3.a FROM t3 WHERE t3.b = 2); QUERY PLAN ——————————————- Hash Join Hash Cond: (t1.a = t2.a) -> Seq Scan on t1 -> Hash -> HashAggregate Group Key: t2.a -> Append -> Seq Scan on t2 Filter: (b = 1) -> Seq Scan on t3 Filter: (b = 2)(11 rows)

多路 OR-of-subqueries 会先合并成一个 UNION 子查询,再作为单一 SubqueryScan 与外层连接,避免每分支独立 SubPlan。

赞 (0)
上一篇 4天前
下一篇 4天前