当前位置: 首页 > news >正文

PostgreSQL 函数式索引使用注意 - 暨非immutable函数不适合索引的原因

标签

PostgreSQL , 表达式 , 函数稳定性 , immutable


背景

PostgreSQL支持表达式索引,但是表达式必须是immutable的,也即是当输入参数不变时,结果是永恒不变的。

因为当表达式涉及的变量不变时,索引本身不会变化。

给个例子,如果我们有一张表存储了商品价格,另一张表存储了商品折扣 ,如果我们想通过折扣后的价格范围搜索符合价格区间的商品ID,可以使用索引吗?

表达式索引,可以。但是前提是:输入一个商品ID时,商品原价永恒不变。

否则原价发生变化就可能出现索引内容与实际不一致的问题。

例子

create extension btree_gist;  

商品表

create table t_item (id int8 primary key, price jsonb);  

折扣表

create table t_item_discount (id int8, ts daterange, country text, discount float4);  

获取商品折后价格的函数

create or replace function get_price(int8,text,float4) returns float8 as $$  
  select (price->>$2)::float8*$3 from t_item where id=$1;  
$$ language sql strict immutable;  

函数索引,immutable函数

create index idx_t_item_discount_1 on t_item_discount using gist (ts, country, get_price(id,country,discount));  

写入商品

insert into t_item values (1, jsonb '{"global":200, "china":150}');  

写入折扣

insert into t_item_discount values (1, daterange('2018-01-01', '2018-01-10'), 'global', 0.4);  

强制索引扫描

set enable_bitmapscan=off;  
set enable_seqscan=off;  
  
postgres=# explain select ctid,get_price(id,country,discount),* from t_item_discount where ts @> '2018-01-01'::date and get_price(id,country,discount)<300 and country='china';  
                                                                QUERY PLAN                                                                   
-------------------------------------------------------------------------------------------------------------------------------------------  
 Index Scan using idx_t_item_discount_1 on t_item_discount  (cost=0.12..8.40 rows=1 width=90)  
   Index Cond: ((ts @> '2018-01-01'::date) AND (country = 'china'::text) AND (get_price(id, country, discount) < '300'::double precision))  
(2 rows)  
  
postgres=# explain select ctid,get_price(id,country,discount),* from t_item_discount where ts @> '2018-01-01'::date and country='china' and get_price(id,country,discount)<300;  
                                                                QUERY PLAN                                                                   
-------------------------------------------------------------------------------------------------------------------------------------------  
 Index Scan using idx_t_item_discount_1 on t_item_discount  (cost=0.12..8.40 rows=1 width=90)  
   Index Cond: ((ts @> '2018-01-01'::date) AND (country = 'china'::text) AND (get_price(id, country, discount) < '300'::double precision))  
(2 rows)  
  
  
  
postgres=# select ctid,get_price(id,country,discount),* from t_item_discount where ts @> '2018-01-01'::date and country='global' and get_price(id,country,discount)<300;  
 ctid  |    get_price     | id |           ts            | country | discount   
-------+------------------+----+-------------------------+---------+----------  
 (0,1) | 80.0000011920929 |  1 | [2018-01-01,2018-01-10) | global  |      0.4  
(1 row)  

但是如果原价变化,索引并不会更新

postgres=# update t_item set price = jsonb '{"global":2000, "china":1500}' where id=1;  
UPDATE 1  

下面的结果显然是错误的

postgres=# select ctid,get_price(id,country,discount),* from t_item_discount where ts @> '2018-01-01'::date and country='global' and get_price(id,country,discount)<300;  
 ctid  |    get_price     | id |           ts            | country | discount   
-------+------------------+----+-------------------------+---------+----------  
 (0,1) | 800.000011920929 |  1 | [2018-01-01,2018-01-10) | global  |      0.4  
(1 row)  
postgres=# update t_item_discount set discount = discount where id=1;  
UPDATE 1  
postgres=# select ctid,get_price(id,country,discount),* from t_item_discount where ts @> '2018-01-01'::date and country='global' and get_price(id,country,discount)<300;  
 ctid  |    get_price     | id |           ts            | country | discount   
-------+------------------+----+-------------------------+---------+----------  
 (0,2) | 800.000011920929 |  1 | [2018-01-01,2018-01-10) | global  |      0.4  
(1 row)  

只有当表达式字段内容发生变化时,相应的表达式才会变化

postgres=# update t_item_discount set discount=discount+0.0000001 where id=1;  
UPDATE 1  
  
postgres=# select ctid,get_price(id,country,discount),* from t_item_discount where ts @> '2018-01-01'::date and country='global' and get_price(id,country,discount)<300;  
 ctid | get_price | id | ts | country | discount   
------+-----------+----+----+---------+----------  
(0 rows)  
  
postgres=# select float4send(discount),* from t_item_discount ;  
 float4send | id |           ts            | country | discount   
------------+----+-------------------------+---------+----------  
 \x3eccccd0 |  1 | [2018-01-01,2018-01-10) | global  |      0.4  
(1 row)  

参考

《PostgreSQL Oracle 兼容性之 - PL/SQL DETERMINISTIC 与PG函数稳定性(immutable, stable, volatile)》

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

《函数稳定性讲解 - retalk PostgreSQL function's [ volatile|stable|immutable ]》

《函数稳定性讲解 - 函数索引思考, pay attention to function index used in PostgreSQL》

《函数稳定性讲解 - Thinking PostgreSQL Function's Volatility Categories》

相关文章:

  • 零基础兴趣或者转行学习Python,我们应该如何入门呢?
  • bartender 9.4 错误消息6670 无法链接到数据库的解决办法
  • JVM G1笔记
  • Linux下切换用户出现su: Authentication failure的解决办法
  • [MicroPython]TPYBoard v102 CAN总线通信
  • Java多线程——AQS框架源码阅读
  • 超大数据下大批量随机键值的查询优化方案
  • node中的npm的使用
  • 未来五年中国本土机器人制造业将显著提升
  • RabbitMq之Work模式
  • (原)记一次CentOS7 磁盘空间大小异常的解决过程
  • python里使用反斜杠转义遇到问题记录
  • AliOS Things 电源管理框架使用说明
  • python三级菜单实例(傻瓜版和进阶版)
  • linux之HTTP服务
  • HTTP中GET与POST的区别 99%的错误认识
  • mysql innodb 索引使用指南
  • MySQL QA
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • Redis 懒删除(lazy free)简史
  • WebSocket使用
  • 初识 beanstalkd
  • 如何设计一个微型分布式架构?
  • 一个项目push到多个远程Git仓库
  • ​io --- 处理流的核心工具​
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • ()、[]、{}、(())、[[]]命令替换
  • (¥1011)-(一千零一拾一元整)输出
  • (1)常见O(n^2)排序算法解析
  • (DenseNet)Densely Connected Convolutional Networks--Gao Huang
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (vue)页面文件上传获取:action地址
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (转)Mysql的优化设置
  • (转)Windows2003安全设置/维护
  • **PHP二维数组遍历时同时赋值
  • .NET 4.0中使用内存映射文件实现进程通讯
  • .NET CF命令行调试器MDbg入门(四) Attaching to Processes
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .net 桌面开发 运行一阵子就自动关闭_聊城旋转门家用价格大约是多少,全自动旋转门,期待合作...
  • .NET/C# 检测电脑上安装的 .NET Framework 的版本
  • .NET值类型变量“活”在哪?
  • .xml 下拉列表_RecyclerView嵌套recyclerview实现二级下拉列表,包含自定义IOS对话框...
  • .考试倒计时43天!来提分啦!
  • [BUG] Authentication Error
  • [C++11 多线程同步] --- 条件变量的那些坑【条件变量信号丢失和条件变量虚假唤醒(spurious wakeup)】
  • [CakePHP] 在Controller中使用Helper
  • [cogs2652]秘术「天文密葬法」
  • [CTSC2014]企鹅QQ
  • [C语言]一维数组二维数组的大小
  • [Head First设计模式]策略模式
  • [HJ56 完全数计算]
  • [html] 动态炫彩渐变背景
  • [java/jdbc]插入数据时获取自增长主键的值