在 WHERE 子句中使用函数时查询速度变慢

Thi*_*yão 5 postgresql performance functions postgresql-12 query-performance

这很快(49ms):

v_cpf_numerico := ext.uf_converte_numerico(new.nr_cpf);

select cd_cliente into v_cd_cliente
from central.cliente where nr_cpf_cnpj = v_cpf_numerico;
Run Code Online (Sandbox Code Playgroud)

这很慢(15 秒):

select cd_cliente into v_cd_cliente
from central.cliente where nr_cpf_cnpj = ext.uf_converte_numerico(new.nr_cpf);
Run Code Online (Sandbox Code Playgroud)

功能:

create or replace function ext.uf_converte_numerico(_input varchar(30)) returns bigint
as
$$
begin
    _input := regexp_replace(_input, '[^0-9]+', '', 'g');

    if _input = '' then
        return null;
    end if;

    return cast(_input as bigint);
end
$$ language plpgsql;
Run Code Online (Sandbox Code Playgroud)

我使用的是 PostgreSQL 12。
为什么第二个变体很慢?

Erw*_*ter 5

考虑这个简化的等价物:

CREATE OR REPLACE FUNCTION ext.uf_converte_numerico(_input varchar(30))
  RETURNS bigint LANGUAGE sql IMMUTABLE PARALLEL SAFE AS
$func$
SELECT NULLIF(regexp_replace(_input, '[^0-9]+', '', 'g'), '')::bigint;
$func$;
Run Code Online (Sandbox Code Playgroud)
  • IMMUTABLE,因为它是,并且劳伦兹解释了原因。

  • PARALLEL SAFE在 Postgres 10 或更高版本中,因为它. 如果没有标签,函数默认为PARALLEL RESTRICTED,这会禁用并行查询。这可能会也可能不会影响显示的查询。但你报告的 15 秒表明你正在大桌子上操作。因此它可以对其他查询产生巨大的影响。

  • LANGUAGE SQL启用函数内联,这对于显示的查询(标记后IMMUTABLE)来说并不重要,但会简化查询计划并提高整体性能。

  • NULLIF作为较小的简化。

旁白:您的输入是varchar(30),它仍然允许出现超出范围的错误bigint。要么考虑varchar(18)确定。或者只是让它text消除无效的限制。