热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PostgreSQL中的函数索引

今天一位同事又问到了这个问题,函数索引.这是一个老问题了.PostgreSQL中函数有三种状态,不稳定,稳定,非常稳定.三种状态分别代表的意思如下:如果一个时间字段上要创建to_char(timestampwithouttimezone,text)的函数索引.是没有办法创建的,报错

今天一位同事又问到了这个问题, 函数索引. 这是一个老问题了.

PostgreSQL中函数有三种状态, 不稳定, 稳定, 非常稳定.

三种状态分别代表的意思如下 : 

如果一个时间字段上要创建to_char(timestamp without time zone, text)的函数索引.
是没有办法创建的, 报错

digoal=> create table t1 (crt_time
timestamp);

CREATE TABLE

digoal=> \set VERBOSITY verbose

digoal=> create index t1_1 on t1
(to_char(crt_time,'yyyy-mm-dd'));

ERROR:  42P17: functions in index expression must be
marked IMMUTABLE

LOCATION:  ComputeIndexAttrs,
indexcmds.c:909

为什么呢?

 看看to_char函数的稳定性状态 : 

digoal=> select proname,provolatile,proargtypes from pg_proc where prOname='to_char';
proname | provolatile | proargtypes
---------+-------------+-------------
to_char | s | 20 25
to_char | s | 23 25
to_char | s | 700 25
to_char | s | 701 25
to_char | s | 1114 25
to_char | s | 1184 25
to_char | s | 1186 25
to_char | s | 1700 25
(8 rows)
digoal=> select oid,typname from pg_type where oid in (20,25,23,700,701,1114,1184,1186,1700);
oid | typname
------+-------------
20 | int8
23 | int4
25 | text
700 | float4
701 | float8
1114 | timestamp
1184 | timestamptz
1186 | interval
1700 | numeric
(9 rows)

为什么函数索引一定要immutable的函数呢?

函数索引一旦建立后, 如果执行计划走索引扫描, PostgreSQL必须确保在任何情况下,
函数得到的结果和建立函数索引时得到的结果是一致的, 否则就会出现走索引检索到的和走全表扫描检索到的结果不一致.
这可是灾难性的BUG.

测试如下 : 

digoal=> create table test (id int,crt_time timestamp(0) with time zone);
CREATE TABLE

-- 正常情况下创建这个函数索引将报错

digoal=> create index idx_test on test (to_char(crt_time,'yyyymmddhh24'));
ERROR: 42P17: functions in index expression must be marked IMMUTABLE
LOCATION: ComputeIndexAttrs, indexcmds.c:909

-- 手工修改了to_char(timestamp with time zone,text)的strict
immutable.

digoal=> \c digoal postgres
You are now connected to database "digoal" as user "postgres".
digoal=# alter function to_char(timestamp with time zone,text) strict immutable;
ALTER FUNCTION

-- 新建函数索引成功

digoal=# \c digoal digoal

You are now connected to database "digoal" as user
"digoal".

digoal=> create index idx_test on test
(to_char(crt_time,'yyyymmddhh24'));

CREATE INDEX

-- 接下来查看当前的TimeZone

digoal=> show TimeZone;
TimeZone
----------
PRC
(1 row)

-- 插入一条测试数据

digoal=> insert into test values (1,'2012-01-01 12:00:00');
INSERT 0 1

-- 在当前的TimeZone下查看to_char(crt_time,'yyyymmddhh24') =
2012010112

digoal=> select to_char(crt_time,'yyyymmddhh24') from test;
to_char
------------
2012010112
(1 row)

-- 把TimeZone改成了GMT

digoal=> set TimeZOne='GMT';
SET

-- 查看to_char(crt_time,'yyyymmddhh24') =
2012010104

-- 同样的参数, 得到的结果不一致. 

digoal=> select to_char(crt_time,'yyyymmddhh24') from test;
to_char
------------
2012010104
(1 row)

-- 接下来在执行计划是走索引时的查询结果. 因为建立索引时,
这个to_char(crt_time,'yyyymmddhh24')是等于2012010112的, 

-- 所以走索引扫描将返回一条记录.

digoal=> explain select * from test where to_char(crt_time,'yyyymmddhh24')='2012010112';
QUERY PLAN
------------------------------------------------------------------------------
Index Scan using idx_test on test (cost=0.00..8.43 rows=10 width=12)
Index Cond: (to_char(crt_time, 'yyyymmddhh24'::text) = '2012010112'::text)
(2 rows)
digoal=> select * from test where to_char(crt_time,'yyyymmddhh24')='2012010112';
id | crt_time
----+------------------------
1 | 2012-01-01 04:00:00+00
(1 row)

-- 接下来严重BUG发生了, 走全表扫描的话同样的SQL, 查询结果没了.

-- 因为走全表扫描的话, 这个值
to_char(crt_time,'yyyymmddhh24') 将被重新运算,
结果是2012010104

digoal=> set enable_indexscan=off;
SET
digoal=> set enable_bitmapscan=off;
SET
digoal=> select * from test where to_char(crt_time,'yyyymmddhh24')='2012010112';
id | crt_time
----+----------
(0 rows)
digoal=> explain analyze select * from test where to_char(crt_time,'yyyymmddhh24')='2012010112';
QUERY PLAN
-------------------------------------------------------------------------------------------------
Seq Scan on test (cost=0.00..39.10 rows=10 width=12) (actual time=0.031..0.031 rows=0 loops=1)
Filter: (to_char(crt_time, 'yyyymmddhh24'::text) = '2012010112'::text)
Total runtime: 0.066 ms
(3 rows)

-- 查询2012010104则有结果, 返回一条记录

digoal=> select * from test where to_char(crt_time,'yyyymmddhh24')='2012010104';
id | crt_time
----+------------------------
1 | 2012-01-01 04:00:00+00
(1 row)

-- 所以在使用函数索引时需要慎重, 创建函数是strict的选择也要慎重.

-- 如果一定要在时间类型上有类似的请求怎么办, 第一使用timestamp without time zone ,
使用immutable的date_trunc函数代替to_char

digoal=> select proname,provolatile,proargtypes from pg_proc where prOname='date_trunc';
proname | provolatile | proargtypes
------------+-------------+-------------
date_trunc | s | 25 1184
date_trunc | i | 25 1186
date_trunc | i | 25 1114
(3 rows)
digoal=> select oid,typname from pg_type where oid in (25,1184,1186,1114);
oid | typname
------+-------------
25 | text
1114 | timestamp
1184 | timestamptz
1186 | interval
(4 rows)
【其他】

1. Thinking PostgreSQL Function's Volatility Categories

仅仅适用timestamp without time zone, 带时区的都会有以上类似的问题.

2.1 以下是个折中方案, 不必修改系统函数to_char的strict, 但是必须符合一定条件才可以冒这个险去使用它.

this is intended behaviour as to_char depends on the LC_MESSAGES
setting

也就是说在使用环境中不会涉及LC_MESSAGES的变动(或者变动后不会影响to_char的结果.)时方可使用.

CREATE OR REPLACE FUNCTION my_to_char(some_time timestamp)
RETURNS text
AS
$BODY$
select to_char($1, 'yyyy-mm-dd');
$BODY$
LANGUAGE sql
IMMUTABLE;

2.2

cast(crt_time as date)

crt_time必须是不带时区的. 否则会有类似问题

digoal=> set TimeZOne='PRC';
SET
digoal=> select cast (timestamp with time zone '2012-01-01 13:00:00+14' as date);
date
------------
2012-01-01
(1 row)
digoal=> set TimeZOne='GMT';
SET
digoal=> select cast (timestamp with time zone '2012-01-01 13:00:00+14' as date);
date
------------
2011-12-31
(1 row)
3. 还有一种方法是增加一列, 用来存储需要建立的函数索引to_char()的结果值. 这样就不需要使用函数索引了,
也不需要考虑函数的strict问题.

推荐阅读
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 2018年人工智能大数据的爆发,学Java还是Python?
    本文介绍了2018年人工智能大数据的爆发以及学习Java和Python的相关知识。在人工智能和大数据时代,Java和Python这两门编程语言都很优秀且火爆。选择学习哪门语言要根据个人兴趣爱好来决定。Python是一门拥有简洁语法的高级编程语言,容易上手。其特色之一是强制使用空白符作为语句缩进,使得新手可以快速上手。目前,Python在人工智能领域有着广泛的应用。如果对Java、Python或大数据感兴趣,欢迎加入qq群458345782。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 学习SLAM的女生,很酷
    本文介绍了学习SLAM的女生的故事,她们选择SLAM作为研究方向,面临各种学习挑战,但坚持不懈,最终获得成功。文章鼓励未来想走科研道路的女生勇敢追求自己的梦想,同时提到了一位正在英国攻读硕士学位的女生与SLAM结缘的经历。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了如何在MySQL中将零值替换为先前的非零值的方法,包括使用内联查询和更新查询。同时还提供了选择正确值的方法。 ... [详细]
  • 本文介绍了brain的意思、读音、翻译、用法、发音、词组、同反义词等内容,以及脑新东方在线英语词典的相关信息。还包括了brain的词汇搭配、形容词和名词的用法,以及与brain相关的短语和词组。此外,还介绍了与brain相关的医学术语和智囊团等相关内容。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Echarts图表重复加载、axis重复多次请求问题解决记录
    文章目录1.需求描述2.问题描述正常状态:问题状态:3.解决方法1.需求描述使用Echats实现了一个中国地图:通过选择查询周期&#x ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • Python字典推导式及循环列表生成字典方法
    本文介绍了Python中使用字典推导式和循环列表生成字典的方法,包括通过循环列表生成相应的字典,并给出了执行结果。详细讲解了代码实现过程。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
author-avatar
福田汽车-唐山万联
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有