当比较值来自我的函数时,为什么Postgres不使用索引?

问题描述 投票:0回答:1

有一张桌子:

create table individual_profile_identity
(
    normalized_document_number varchar,
    normalized_name            varchar,
    birth_date                 date
);

里面有索引:

create index individual_profile_identity_name_birth_dt_doc_idx
    on individual_profile_identity (
        normalized_name varchar_pattern_ops,
        birth_date,
        normalized_document_number
    );

有一个函数可以标准化

document_number
name
字段:

create function rf_normalize_name(par_name character varying) returns character varying
    language plpgsql
as
$$
BEGIN
    RETURN nullif(upper(translate(par_name, 'A !@#$%^&*()_-+={}[]:;"''<>,.?/|\`~', 'A')), '0');
END
$$;

查询:

explain
select *
from individual_profile_identity
where normalized_name=rf_normalize_name('John Donne')
  and birth_date='2000-01-01'
  and normalized_document_number=rf_normalize_name('1234 567890');

计划:

Index Scan using individual_profile_identity_name_birth_dt_doc_idx on individual_profile_identity  (cost=0.41..1935.18 rows=1 width=173)
  Index Cond: (birth_date = '2000-01-01'::date)
  Filter: (((normalized_name)::text = (rf_normalize_name('John Donne'::character varying))::text) AND ((normalized_document_number)::text = (rf_normalize_name('1234 567890'::character varying))::text))

如您所见,它使用索引,但仅包含

birth_date

Index Cond: (birth_date = '2000-01-01'::date)

我完全不明白为什么 postgres 不会首先运行

rf_normalize_name
函数,然后才将索引应用于所有 3 个字段

有人可以解释一下这种行为吗?

postgresql indexing
1个回答
0
投票

如果您没有另外指定,

CREATE FUNCTION
语句默认会创建一个
VOLATILE
函数。这“表明即使在单个表扫描中函数值也可能发生变化,因此无法进行优化”,迫使Postgres在每一行上调用该函数并且无法使用索引。

你应该

CREATE FUNCTION rf_normalize_name(par_name varchar) RETURNS varchar
    LANGUAGE SQL
    IMMUTABLE
    PARALLEL SAFE
    RETURN nullif(upper(translate(par_name, 'A !@#$%^&*()_-+={}[]:;"''<>,.?/|\`~', 'A')), '0');

那么您的查询将使用所有三列上的索引。

© www.soinside.com 2019 - 2024. All rights reserved.