You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL非外键引用列JOIN如何创建手动依赖统计

问题场景

假设有如下表结构:

CREATE TABLE main
(
    id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY
);

CREATE TABLE apple
(
    id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    main_id INTEGER NOT NULL REFERENCES main(id)
);

CREATE TABLE orange
(
    id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    main_id INTEGER NOT NULL REFERENCES main(id)
);

CREATE TABLE main_history
(
    id INTEGER NOT NULL,
    history_valid_from TIMESTAMPTZ NOT NULL,
    history_valid_to TIMESTAMPTZ
);

CREATE TABLE apple_history
(
    id INTEGER NOT NULL,
    main_id INTEGER NOT NULL REFERENCES main(id), -- main_history 无主键
    history_valid_from TIMESTAMPTZ NOT NULL,
    history_valid_to TIMESTAMPTZ
);

CREATE TABLE orange_history
(
    id INTEGER NOT NULL,
    main_id INTEGER NOT NULL REFERENCES main(id), -- main_history 无主键
    history_valid_from TIMESTAMPTZ NOT NULL,
    history_valid_to TIMESTAMPTZ
);

其中main为根表,apple、orange两张表通过外键引用main表;99%的场景下,每条main表记录对应的子表记录最多为20条。

性能问题表现

查询历史数据时出现严重性能问题,原查询语句如下:

SELECT * FROM main_history
LEFT JOIN apple_history ON apple_history.main_id = main_history.id AND
  apple_history.history_valid_from <= '2021-12-20T10:46:52.482620Z' AND
  (apple_history.history_valid_to IS NULL OR '2021-12-20T10:46:52.482620Z' < apple_history.history_valid_to)
LEFT JOIN orange_history ON orange_history.main_id = main_history.id AND
  orange_history.history_valid_from <= '2021-12-20T10:46:52.482620Z' AND
  (orange_history.history_valid_to IS NULL OR '2021-12-20T10:46:52.482620Z' < orange_history.history_valid_to)
WHERE
  main_history.id IN (1,2,3,4) AND
  main_history.history_valid_from <= '2021-12-20T10:46:52.482620Z' AND
  (main_history.history_valid_to IS NULL OR '2021-12-20T10:46:52.482620Z' < main_history.history_valid_to)

核心问题:当前关联使用的是无外键引用约束的列,PostgreSQL对返回行数的估算偏差最高可达20000倍。

已尝试的临时方案

执行以下配置关闭哈希连接和合并连接:

SET enable_hashjoin = off; SET enable_mergejoin = off

由于实际数据量很小,执行该配置后查询耗时降至100毫秒。

如果将查询改写为直接关联main表而非main_history表的形式(修正了原语句中不存在的表引用笔误):

SELECT * FROM main
LEFT JOIN apple_history ON apple_history.main_id = main.id AND
  apple_history.history_valid_from <= '2021-12-20T10:46:52.482620Z' AND
  (apple_history.history_valid_to IS NULL OR '2021-12-20T10:46:52.482620Z' < apple_history.history_valid_to)
LEFT JOIN orange_history ON orange_history.main_id = main.id AND
  orange_history.history_valid_from <= '2021-12-20T10:46:52.482620Z' AND
  (orange_history.history_valid_to IS NULL OR '2021-12-20T10:46:52.482620Z' < orange_history.history_valid_to)
WHERE
  main.id IN (1,2,3,4)

此时优化器可以使用原有统计信息,查询执行正常无性能问题。

待解决问题

是否可以为无REFERENCES外键约束的JOIN关联列创建手动统计信息?目标是无需强制指定连接策略,即可直接使用main_history作为主表完成高效查询。


内容的提问来源于stack exchange,提问作者damike

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 03:33:33