暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL 函数稳定性与constraint_excluded分区表逻辑推理过滤的CASE

digoal 2016-05-31
629

作者

digoal

日期

2016-05-31

标签

PostgreSQL , 函数稳定性 , 过滤分区 , constraint_excluded


背景

PostgreSQL 函数稳定性我在以前写过一些文章来讲解,而且在PG的优化器中,也有大量的要用函数稳定性来做出优化选择的地方。

http://www.tudou.com/programs/view/p6E3oQEsZv0/

本文要分享的这个CASE也和函数稳定性有关,当我们在使用分区表时,PostgreSQL可以根据分区表的约束,以及用户在SQL中提供的条件进行比对,通过逻辑推理过滤掉一些不需要扫描的表。

逻辑推理在前面也讲过。

《PostgreSQL 优化器逻辑推理能力 源码解析》

这里先抛一个结论,约束检查时,条件中如果有函数,必须是immutable级别的,这样的条件才能进行逻辑推理,过滤掉不需要查询的表。

为什么stable不行呢?

因为执行计划是有缓存的,过滤掉的查询不需要进入执行计划的生成,所以必须保证被过滤的函数在多次调用时得到的结果是一致的,这样可以保证生成的执行计划和不过滤生成的执行计划在输入同样条件时,得到的结果也是一致的。

OK那么就来看个例子吧:

````
postgres=# create table p1(id int, t int);
CREATE TABLE
postgres=# create table c1(like p1) inherits(p1);
NOTICE: merging column "id" with inherited definition
NOTICE: merging column "t" with inherited definition
CREATE TABLE
postgres=# create table c2(like p1) inherits(p1);
NOTICE: merging column "id" with inherited definition
NOTICE: merging column "t" with inherited definition
CREATE TABLE
postgres=# select to_timestamp(123);
to_timestamp


1970-01-01 08:02:03+08
(1 row)

postgres=# alter table c1 add constraint ck check(to_char(to_timestamp(t::double precision), 'yyyymmdd'::text) >= '20150101'::text AND to_char(to_timestamp(t::double precision), 'yyyymmdd'::text) < '20150102'::text);
ALTER TABLE
postgres=# alter table c2 add constraint ck check(to_char(to_timestamp(t::double precision), 'yyyymmdd'::text) >= '20150102'::text AND to_char(to_timestamp(t::double precision), 'yyyymmdd'::text) < '20150103'::text);
ALTER TABLE
postgres=# explain select * from p1 where to_char((to_timestamp(t::double precision)), 'yyyymmdd'::text)='20150101'::text;
QUERY PLAN


Append (cost=0.00..110.40 rows=23 width=8)
-> Seq Scan on p1 (cost=0.00..0.00 rows=1 width=8)
Filter: (to_char(to_timestamp((t)::double precision), 'yyyymmdd'::text) = '20150101'::text)
-> Seq Scan on c1 (cost=0.00..55.20 rows=11 width=8)
Filter: (to_char(to_timestamp((t)::double precision), 'yyyymmdd'::text) = '20150101'::text)
-> Seq Scan on c2 (cost=0.00..55.20 rows=11 width=8)
Filter: (to_char(to_timestamp((t)::double precision), 'yyyymmdd'::text) = '20150101'::text)
(7 rows)
```

原因是这两个函数都是stable的

List of functions Schema | Name | Result data type | Argument data types | Type | Security | Volatility | Owner | Language | Source code | Description ------------+--------------+--------------------------+---------------------+--------+----------+------------+----------+----------+--------------------+------------------------------------------ pg_catalog | to_timestamp | timestamp with time zone | double precision | normal | invoker | stable | postgres | internal | float8_timestamptz | convert UNIX epoch to timestamptz pg_catalog | to_char | text | timestamp with time zone, text | normal | invoker | stable | postgres | internal | timestamptz_to_char | format timestamp with time zone to text

stable的函数能保证在一个事务中,使用同样的参数多次调用得到的结果一致,但是不能保证任意时刻。

例如一个会话中,多次调用可能不一致。(那么有执行计划缓存的话,过滤掉这样的子分区就危险了)。

这两个函数为什么是stable 的呢,因为它和一些环境因素有关。

好了,那么了解这个之后,就知道为什么前面的查询没有排除这些约束了。

解决办法 :

1. 新增用户定义的函数,改SQL以及约束。

```
create or replace function im_to_char(timestamptz,text) returns text as $$
select to_char($1,$2);
$$ language sql immutable;

create or replace function im_to_timestamp(double precision) returns timestamptz as $$
select to_timestamp($1);
$$ language sql immutable;

postgres=# alter table c1 drop constraint ck;
ALTER TABLE
postgres=# alter table c2 drop constraint ck;
ALTER TABLE

postgres=# alter table c1 add constraint ck check(im_to_char(im_to_timestamp(t::double precision), 'yyyymmdd'::text) >= '20150101'::text AND im_to_char(im_to_timestamp(t::double precision), 'yyyymmdd'::text) < '20150102'::text);
ALTER TABLE
postgres=# alter table c2 add constraint ck check(im_to_char(im_to_timestamp(t::double precision), 'yyyymmdd'::text) >= '20150102'::text AND im_to_char(im_to_timestamp(t::double precision), 'yyyymmdd'::text) < '20150103'::text);
ALTER TABLE

postgres=# explain select * from p1 where im_to_char((im_to_timestamp(t::double precision)), 'yyyymmdd'::text)='20150101'::text;
QUERY PLAN


Append (cost=0.00..1173.90 rows=12 width=8)
-> Seq Scan on p1 (cost=0.00..0.00 rows=1 width=8)
Filter: (im_to_char(im_to_timestamp((t)::double precision), 'yyyymmdd'::text) = '20150101'::text)
-> Seq Scan on c1 (cost=0.00..1173.90 rows=11 width=8)
Filter: (im_to_char(im_to_timestamp((t)::double precision), 'yyyymmdd'::text) = '20150101'::text)
(5 rows)
```

2. 一个冒险的做法是直接修改这两个函数的稳定性。

alter function to_timestamp(double precision) immutable; alter function to_char(timestamptz, text) immutable;

搞定

```
postgres=# explain select * from p1 where to_char((to_timestamp(t::double precision)), 'yyyymmdd'::text)='20150101'::text;
QUERY PLAN


Append (cost=0.00..55.20 rows=12 width=8)
-> Seq Scan on p1 (cost=0.00..0.00 rows=1 width=8)
Filter: (to_char(to_timestamp((t)::double precision), 'yyyymmdd'::text) = '20150101'::text)
-> Seq Scan on c1 (cost=0.00..55.20 rows=11 width=8)
Filter: (to_char(to_timestamp((t)::double precision), 'yyyymmdd'::text) = '20150101'::text)
(5 rows)
```

PostgreSQL 许愿链接

您的愿望将传达给PG kernel hacker、数据库厂商等, 帮助提高数据库产品质量和功能, 说不定下一个PG版本就有您提出的功能点. 针对非常好的提议,奖励限量版PG文化衫、纪念品、贴纸、PG热门书籍等,奖品丰富,快来许愿。开不开森.

9.9元购买3个月阿里云RDS PostgreSQL实例

PostgreSQL 解决方案集合

德哥 / digoal's github - 公益是一辈子的事.

digoal's wechat

文章转载自digoal,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论