我在PostgreSQL 12中使用声明式分区:
create table foo (
id integer not null,
date timestamp not null,
count integer default 0,
primary key (id, date)
)
partition by RANGE (date);
通过这个简单的请求,Postgres搜索了唯一一个正确的分区-这很棒:
select sum(foo.count) as total,
date_trunc('day', foo.date) as day_date
from foo
where foo.date between '2023-01-01' and '2023-01-02'
group by day_date
然而,当我使用generate_series()
修改这个查询时,Postgres开始搜索所有分区:
with times as (
select generate_series('2023-01-01 12:00', '2023-01-02 16:00', '1 hour') as date1,
generate_series('2023-01-01 16:00', '2023-01-02 20:00', '1 hour') as date2
)
select sum(foo.count) as total,
times.date1
from times join foo on foo.date between times.date1 and times.date2
group by times.date1;
我假设,查询规划器计划在执行查询之前忽略一些分区,只是查看它。这意味着,我不能使用动态生成的参数进行筛选?
是否有一些方法来解决这个问题?
1条答案
按热度按时间mbjcgjjk1#
最有可能的情况是,您错过了第二个查询也要修剪分区。
先修复你的DB设计和查询
PRIMARY KEY (ts, id)
。功能相同,但ts
作为PK索引中的前导列会对您的查询产生大差异。参见:BETWEEN
,它包括上限,而分区边界排除上限(就像对时间戳的任何合理范围操作一样)。手册:在创建范围分区时,用
FROM
指定的下限是一个包含性边界,而用TO
指定的上限是一个独占性边界。否则,您会因不匹配的边界而引起各种混乱。
说明它(可能)如何工作
我们需要打开**
enable_partition_pruning
-显然您已经打开了,而且是默认的。这包括两种不同的分区修剪方法**:一次是在计划查询时,另一次是在执行期间。手册:启用或禁用查询计划器从查询计划中消除已分区表的分区的功能。这还控制规划器生成查询计划的能力,该查询计划允许查询执行器在查询执行期间移除(忽略)分区。默认值为
on
。详情见第5.11.4节。您的第一个带有常量过滤器的简单查询可以使用第一种方法,因为值在计划时是已知的。第二个查询的情况并非如此:值稍后生成。所以只有第二种变体是可能的-它是用Postgres 11添加的。发行说明:
允许在查询执行期间消除分区(大卫罗利,Beena Emerson)
以前,分区消除只发生在计划时,这意味着许多连接和准备好的查询不能使用分区消除。
你声明了Postgres 12,所以它应该可以工作。
另外,升级到最新版本也不会有什么坏处。发布说明Postgres 13:
允许在更多情况下修剪分区(Yuzuko Hosoya,Amit Langote,Álvaro Herrera)
在pgsql-hackers上的这个线程中讨论了更改。
但是您的简单查询也应该在Postgres 12中进行分区修剪。
在执行期间进行分区修剪的第二种方法没有(不能)显示在来自
EXPLAIN
的简单查询计划中。您必须使用EXPLAIN (ANALYZE)
进行测试,然后您将看到**(never executed)
始终被修剪的分区。手册:[...]要确定在此阶段是否修剪了分区,需要仔细检查
EXPLAIN ANALYZE
输出中的循环属性。对应于不同分区的子计划可以具有不同的值,这取决于它们中的每一个在执行期间被修剪了多少次。如果每次都修剪,则一些可能显示为(never executed)
。检查此fiddle中的查询计划!
您的first的查询计划(即使是普通的
EXECUTE
)(改进!)查询显示规划期间的分区修剪:开始时仅涉及第一分区。您的秒**的查询计划(即使是普通的
EXECUTE
)(改进!)查询显示了分区3的(never executed)
,它一直被修剪,但分区1和2没有,它们只在一些迭代中被修剪。相关内容: