有一张桌子:
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 个字段
有人可以解释一下这种行为吗?
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');
那么您的查询将使用所有三列上的索引。