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

简介:

PostgreSQL 函数稳定性我在以前写过一些文章来讲解,而且在PG的优化器中,也有大量的要用函数稳定性来做出优化选择的地方。
http://www.tudou.com/programs/view/p6E3oQEsZv0/
本文要分享的这个CASE也和函数稳定性有关,当我们在使用分区表时,PostgreSQL可以根据分区表的约束,以及用户在SQL中提供的条件进行比对,通过逻辑推理过滤掉一些不需要扫描的表。
逻辑推理在前面也讲过。
https://yq.aliyun.com/articles/6821

这里先抛一个结论,约束检查时,条件中如果有函数,必须是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)
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
存储 关系型数据库 数据库
【赵渝强老师】PostgreSQL的逻辑存储结构
PostgreSQL的逻辑存储结构包括数据库集群、数据库、表空间、段、区、块等。每个对象都有唯一的对象标识符OID,并存储于相应的系统目录表中。集群由单个服务器实例管理,包含多个数据库、用户及对象。表空间是数据库的逻辑存储单元,用于组织逻辑相关的数据结构。段是分配给表、索引等逻辑结构的空间集合,区是段的基本组成单位,而块则是最小的逻辑存储单位。
567 2
【赵渝强老师】PostgreSQL的逻辑存储结构
|
JSON 关系型数据库 MySQL
Mysql(5)—函数
MySQL提供了许多内置的函数以帮助用户进行数据操作和分析。这些函数可以分为几类,包括聚合函数、字符串函数、数值函数、日期和时间函数、控制流函数等。
532 1
Mysql(5)—函数
|
存储 关系型数据库 测试技术
拯救海量数据:PostgreSQL分区表性能优化实战手册(附压测对比)
本文深入解析PostgreSQL分区表的核心原理与优化策略,涵盖性能痛点、实战案例及压测对比。首先阐述分区表作为继承表+路由规则的逻辑封装,分析分区裁剪失效、全局索引膨胀和VACUUM堆积三大性能杀手,并通过电商订单表崩溃事件说明旧分区维护的重要性。接着提出四维设计法优化分区策略,包括时间范围分区黄金法则与自动化维护体系。同时对比局部索引与全局索引性能,展示后者在特定场景下的优势。进一步探讨并行查询优化、冷热数据分层存储及故障复盘,解决分区锁竞争问题。
1840 2
|
SQL 关系型数据库 MySQL
MySQL 常用函数
我们这次全面梳理 MySQL 中的常用函数,涵盖 聚合函数、字符串函数、日期时间函数、数学函数 和 控制流函数 等五大类。每类函数均配有语法说明与实用示例,帮助读者提升数据处理能力,如统计分析、文本处理、日期计算、条件判断等。文章结尾提供了丰富的实战练习,帮助读者巩固和应用函数技巧,是进阶 SQL 编程与数据分析的实用工具手册。
1051 2
|
SQL 关系型数据库 MySQL
【YashanDB知识库】MySQL field 函数的改写方法
【YashanDB知识库】MySQL field 函数的改写方法
|
SQL 关系型数据库 MySQL
Mysql-常用函数及其用法总结
以上列举了MySQL中一些常用的函数及其用法。这些函数在日常的数据库操作中非常实用,能够简化数据查询和处理过程,提高开发效率。掌握这些函数的使用方法,可以更高效地处理和分析数据。
519 19
|
SQL 关系型数据库 MySQL
【MySQL基础篇】盘点MySQL常用四大类函数
本文介绍了MySQL中的四大类常用函数:字符串函数、数值函数、日期函数和流程函数。
【MySQL基础篇】盘点MySQL常用四大类函数
|
SQL 关系型数据库 MySQL
【YashanDB知识库】MySQL field 函数的改写方法
本文来自YashanDB官网,介绍将MySQL的FIELD函数改写到YashanDB的方法。MySQL中,FIELD函数用于自定义排序;而在YashanDB中,可使用DECODE或CASE语句实现类似功能。示例展示对表`t1`按指定顺序排序的过程,提供两种改写方式,结果均符合预期。
|
数据采集 关系型数据库 MySQL
MySQL常用函数:IF、SUM等用法
本文介绍了MySQL中常用的IF、SUM等函数及其用法,通过具体示例展示了如何利用这些函数进行条件判断、数值计算以及复杂查询。同时,文章还提到了CASE WHEN语句和其他常用函数,如COUNT、AVG、MAX/MIN等,强调了它们在数据统计分析、数据清洗和报表生成中的重要性。
|
关系型数据库 MySQL
MySQL常见函数第一期,你都用过哪些呢 ?
本期介绍10个常用的MySQL函数:字符串连接(CONCAT)、提取子字符串(SUBSTRING)、获取字符串长度(LENGTH)、转换大小写(UPPER、LOWER)、去除空格(TRIM)、替换字符串(REPLACE)、查找子字符串位置(INSTR)、带分隔符的字符串连接(CONCAT_WS)以及获取当前日期时间(NOW)。
467 8

推荐镜像

更多