Rj_*_*j_N 4 postgresql index coalesce postgresql-9.6
我使用的是 PostgreSQL V9.6.11
表DDL:
CREATE TABLE test_c (
insrt_prcs_id bigint NOT NULL,
updt_prcs_id bigint, src_sys_id integer NOT NULL,
load_dttm timestamp(6) with time zone NOT NULL,
updt_dttm timestamp(6) without time zone);
Run Code Online (Sandbox Code Playgroud)
我试图index
为下面的查询创建一个:
SELECT *
FROM test_c
WHERE COALESCE(u_dttm,l_dttm) > '2020-04-10 15:29:44.596311-07'
AND COALESCE(u_dttm,l_dttm) <= '2020-04-11 15:29:44.596311-07'
Run Code Online (Sandbox Code Playgroud)
创建index
为:
create index idx_test_c on test_c(COALESCE((updt_dttm, load_dttm)))
Run Code Online (Sandbox Code Playgroud)
但查询计划器没有扫描索引:
EXPLAIN ANALYZE
SELECT *
FROM test_c
WHERE COALESCE(u_dttm,l_dttm) > '2020-04-10 15:29:44.596311-07'
AND COALESCE(u_dttm,l_dttm) <= '2020-04-11 15:29:44.596311-07'
Run Code Online (Sandbox Code Playgroud)
Seq Scan on test_c as test_c (cost=0..1857.08 rows=207 width=496) (actual=5.203..5.203 rows=0 loops=1)
Filter: ((COALESCE((test_c.updt_dttm)::timestamp with time zone, test_c.load_dttm) > '2020-04-10 15:29:44.596311-07'::timestamp with time zone) AND (COALESCE((test_c.updt_dttm)::timestamp with time zone, test_c.load_dttm) <= '2020-04-11 15:29:44.596311-07'::timestamp with time zone))
Rows Removed by Filter: 41304
Run Code Online (Sandbox Code Playgroud)
为什么索引扫描没有发生?
去掉一对不正确的括号:
CREATE INDEX idx_test_c ON test_c(COALESCE(updt_dttm, load_dttm));
Run Code Online (Sandbox Code Playgroud)
db<>在这里摆弄
您的方式是有效地索引复合值 (updt_dttm, load_dttm)
,COALESCE
而不执行任何操作。
添加的表定义揭示了您的第二个问题:
CREATE TABLE test_c (
insrt_prcs_id bigint NOT NULL
, updt_prcs_id bigint
, src_sys_id integer NOT NULL
, load_dttm timestamp(6) with time zone NOT NULL
, updt_dttm timestamp(6) without time zone -- !!!
);
Run Code Online (Sandbox Code Playgroud)
为什么要对load_dttm
和使用不同的数据类型updt_dttm
?解决这个问题,第二个问题就消失了。我建议:
CREATE TABLE test_c (
-- ...
, load_dttm timestamp with time zone NOT NULL
, updt_dttm timestamp with time zone -- !!!
);
Run Code Online (Sandbox Code Playgroud)
为什么?
你得到这个错误:
错误:索引表达式中的函数必须标记为 IMMUTABLE
..因为COALESCE
必须返回一种数据类型。timestamp with time zone
( timestamtz
) 是“首选类型”,因此updt_dttm timestamp
被强制为timestamptz
,它使用的函数取决于timezone
当前会话的设置,因此不是 IMMUTABLE
。并且索引表达式不能涉及非 IMMUTABLE 函数。
有关的:
您可以通过对时区进行硬编码来使索引与原始(损坏的)表设计一起工作 -'Europe/Vienna'
在我的示例中:
CREATE INDEX ON test_c(COALESCE(updt_dttm AT TIME ZONE 'Europe/Vienna', load_dttm));
Run Code Online (Sandbox Code Playgroud)
只是一个概念证明。希望使用该索引的查询必须使用相同的表达式。如果没有必要,不要去那里。相反,请修复您的表定义。