PostgreSQL分区范围 - 奇怪的行为

时间:2017-01-16 17:14:14

标签: 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)

等......

当我执行以下查询时,它正常工作并在右分区表中搜索:

select * from t1
where date_id >= 20161201
and date_id <= 20161231;

但是,当我执行以下查询时,由于某种原因,它会搜索所有分区表中的信息:

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;

我认为PostgreSQL在where子句中收到函数时出错了,我是对的吗?

如何更改此行为?

谢谢!

2 个答案:

答案 0 :(得分:1)

系统限制特定分区的能力称为“约束排除”(目前,Postgres没有内置的“分区”概念,只是构建它们所需的部分)。

要理解的重要一点是,此机制内置于查询规划器中。如果您在查询中运行EXPLAIN ...,您将在计划中看到扫描每个子表(分区)的节点。

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

现在,您的第二个查询的问题是规划器不知道to_char('2016-12-01'::date, 'YYYYMMDD')::int的值是什么 - 它还没有运行该功能。 (你和我知道它看起来相当于常量值20161201,但对于像Postgres的查询规划器这样的编译器来说,这是一个复杂的逻辑跳跃!)所以它无法证明不会返回任何行从任何特定的表格中排除它们。

the manual page on partitioning的底部,有一个提示:

  

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

答案 1 :(得分:1)

如果你需要计算参数,那就用它来构建它:

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);