PostgreSQL分区范围-奇怪的行为

PostgreSQL分区范围-奇怪的行为,postgresql,partitioning,Postgresql,Partitioning,我使用整数范围对表(t1)进行分区,使用如下检查: CONSTRAINT t1_201611_check CHECK (date_id >= 20161101 AND date_id <= 20161130) CONSTRAINT t1_201612_check CHECK (date_id >= 20161201 AND date_id <= 20161231) 约束t1\u 201611\u检查检查(日期\u id>=20161101,日期\u id=2016120

我使用整数范围对表(t1)进行分区,使用如下检查:

CONSTRAINT t1_201611_check CHECK (date_id >= 20161101 AND date_id <= 20161130)
CONSTRAINT t1_201612_check CHECK (date_id >= 20161201 AND date_id <= 20161231)
约束t1\u 201611\u检查检查(日期\u id>=20161101,日期\u id=20161201,日期\u id=20161201 日期_id=to_char('2016-12-01'::日期,'YYYYMMDD')::int
系统限制特定分区的能力被称为“约束排除”(目前,Postgres没有内置的“分区”概念,只是构建分区所需的部分)

需要了解的重要一点是,该机制内置于查询计划器中。如果对查询运行
EXPLAIN…
,您将看到计划中用于扫描每个子表(分区)的节点

如果子表上的约束意味着它不可能包含任何相关行,“约束排除”所做的就是删除这些节点。这一切都发生在查询执行之前

现在,第二个查询的问题是,计划者不知道
to_char('2016-12-01'::date,'YYYYMMDD')::int的值是什么-它还没有运行该函数。(您和我都知道,通过查看它,它相当于常量值
20161201
,但对于Postgres的查询规划器这样的编译器来说,这是一个复杂的逻辑飞跃!)因此它不能证明任何特定表都不会返回任何行来排除它们

在的底部,有以下提示:

保持分区约束简单,否则规划者可能无法证明分区不需要访问。使用简单的相等条件进行列表分区,或使用简单的范围测试进行范围分区,如前面的示例所示。一个好的经验法则是分区约束只应包含comp使用B-树可索引运算符将分区列生成常量


如果需要计算出的参数,则进行动态构建:

create or replace function f(_start_date date, _end_date date)
returns setof t1 as $f$

begin
    return query execute $$ 
        select *
        from t1
        where date_id >= $1 and date_id <= $2
    $$ using to_char(_start_date, 'YYYYMMDD')::int, to_char(_end_date, 'YYYYMMDD')::int
    ;
end;

$f$ language plpgsql;

select *
from f('2016-12-01'::date, '2016-12-31'::date);
创建或替换函数f(\u开始日期,\u结束日期)
将t1的集合返回为$f$
开始
返回查询执行$$
挑选*
从t1开始

where date\u id>=$1和date\u id谢谢你的回答!我认为问题在于to\u char不是一个不可变的函数。这一点现在很清楚了!你知道为什么在下面的示例中查询计划器没有在所有表中搜索吗?只在正确的表中?
选择*from t1 where date\u id>=replace('2017-01-01','-',''::整数和日期_id@LeandroGuimarães嗯,文件上说“规划者可能无法证明……”;不是“不会……”;因此,很明显,在某些情况下,它可以预先计算常量值并将其与约束匹配,但不能保证它会这样做。查询计划器是一个极其复杂的软件,随着代码以不同的方式得到改进,在不同的版本之间,工作的确切情况可能会有所不同。
select * from t1
where date_id >= to_char('2016-12-01'::date, 'YYYYMMDD')::int
and date_id <= to_char('2016-12-31'::date, 'YYYYMMDD')::int;
create or replace function f(_start_date date, _end_date date)
returns setof t1 as $f$

begin
    return query execute $$ 
        select *
        from t1
        where date_id >= $1 and date_id <= $2
    $$ using to_char(_start_date, 'YYYYMMDD')::int, to_char(_end_date, 'YYYYMMDD')::int
    ;
end;

$f$ language plpgsql;

select *
from f('2016-12-01'::date, '2016-12-31'::date);