如何告诉 SQL 规划器主查询中的 WHERE 条件应该在子查询中执行?

问题描述 投票:0回答:1

我正在使用 Postgres 14.x 顺便说一句。

我有一个可以观察的复杂查询,旁边是 EXPLAIN ANALYZE,这里

history
表的目的是记录
public
表中每一行的变化,以便我们随时了解过去的状态。该函数基本上为您提供了一个表,表示在提供的时间戳处
public
表的状态。

计算整个表过去的样子非常昂贵,但我的目的是通过使用该函数以及我想要的行 ID 的过滤器,然后只计算该行。但这并没有发生。

速度非常慢。应该需要几毫秒。但是,它不是在第一次扫描时执行过滤器

"id" = 'fd18211b-a400-49ed-a723-9648ab05ca4f'
来大大减少获取的行数,从而使聚合节点更快,而是在最后执行。

如果我将

COALESCE(past.id, present.id) AS id
替换为
past.id AS id
present.id AS id
,它就可以修复,如 here 所示,但它不正确,因为
past.id
present.id
可能是
NULL

但是如果

past.id
NULL
则意味着所有
past.*
都是 NULL。与
present
相同。因此将过滤器应用于子查询仍然是有效的。

那么我怎样才能在保持 COALESCE 的同时通知规划器应该将过滤器应用于子查询呢?

sql postgresql query-optimization postgresql-performance query-planner
1个回答
0
投票

除非 Postgres 可以在调用查询中“内联”函数(这对于返回集合的函数来说通常是不可能的),否则对于查询规划器来说它就是一个黑匣子。函数内部的所有内容都是与外部查询分开计划和执行的,并且在继续进行外部查询之前具体化结果。对你来说最坏的情况。 要尽早对 ID 应用最重要的过滤器,请将其传递给函数,然后将其应用到根。

另外,当您只需要最新的非空值时,不要

array-agg()

所有行。我展示了一个使用

UNION ALL
而不是
FULL OUTER JOIN
的解决方案,在子查询中订购一次,并与附加
first_last_agg
模块中的函数
first()
进行聚合,您必须先安装该模块。此相关答案中的详细信息和替代方案:

    为多列选择最近的非空值
  • CREATE OR REPLACE FUNCTION public.select_version_of_project(_id uuid, _ts timestamptz) RETURNS TABLE ( id uuid , folder_id uuid , name text --, etc. ) LANGUAGE sql STABLE AS $func$ SELECT p.id , first(p.folder_id) FILTER (WHERE p.folder_id IS NOT NULL) AS folder_id , first(p.name) FILTER (WHERE p.name IS NOT NULL) AS name -- , etc. FROM ( SELECT * FROM history.project h WHERE h.id = _id AND h.updated_at <= _ts -- ? UNION ALL SELECT * FROM public.project p WHERE p.id = _id AND p.updated_at <= _ts -- ? ORDER BY updated_at DESC -- !! I assume you want the latest state ) p GROUP BY 1; $func$;
致电:

SELECT * FROM public.select_version_of_project ('fd18211b-a400-49ed-a723-9648ab05ca4f' , '2024-03-08 08:31:08.280+0') -- timestamptz !

我对 
created_at

updated_at
的过滤器并不完全清楚。您可能需要在那里做更多事情。
确保在 

history.project(id, updated_at)

上有 多列索引

不确定 
public.project
- 只需
(id)
上的索引就足够了,并且应该已经作为 PK 索引存在。
传递声明的 

timestamptz

值,否则您可能会得到令人惊讶的转换。

    

© www.soinside.com 2019 - 2024. All rights reserved.