postgresql 为什么我没有看到基于generate_series()的查询的分区修剪?

oxiaedzo  于 2023-06-29  发布在  PostgreSQL
关注(0)|答案(1)|浏览(138)

我在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;

我假设,查询规划器计划在执行查询之前忽略一些分区,只是查看它。这意味着,我不能使用动态生成的参数进行筛选?
是否有一些方法来解决这个问题?

mbjcgjjk

mbjcgjjk1#

最有可能的情况是,您错过了第二个查询也要修剪分区。

先修复你的DB设计和查询

  • 不要使用误导性的名称“日期”作为时间戳。用“ts”代替。
  • 将主键更改为PRIMARY KEY (ts, id)。功能相同,但ts作为PK索引中的前导列会对您的查询产生差异。参见:
  • Is a composite index also good for queries on the first field?
  • 不要对时间戳使用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没有,它们只在一些迭代中被修剪。
相关内容:

相关问题