web-dev-qa-db-ja.com

WHERE句で関数を使用するとクエリが遅くなる

これは高速です(49ミリ秒):

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;

これは遅い(15秒):

select cd_cliente into v_cd_cliente
from central.cliente where nr_cpf_cnpj = ext.uf_converte_numerico(new.nr_cpf);

関数:

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;

PostgreSQL 12を使用しています。
2番目のバリアントが遅いのはなぜですか?

3
Thiago Sayão

この単純化された同等のものを検討してください:

_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$;
_
  • IMMUTABLE 、それはであり、理由により ローレンツは説明した だからです。

  • _PARALLEL SAFE_ Postgres 10以降では、isであるためです。ラベルがない場合、関数はデフォルトで_PARALLEL RESTRICTED_になり、並列クエリが無効になります。これは、表示中のクエリに影響する場合と影響しない場合があります。しかし、報告した15秒は、大きなテーブルを操作していることを示しています。そのため、他のクエリでhugeの違いが生じる可能性があります。

  • _LANGUAGE SQL_ 有効にするには 関数のインライン化 、これは表示されているクエリではそれほど重要ではありませんが(IMMUTABLEとラベル付けした後)、クエリプランを簡素化し、全体的なパフォーマンスを向上させます。

  • NULLIF 単純化してください。

余談ですが、入力はvarchar(30)ですが、bigintout of rangeエラーは許容されます。確実にvarchar(18)を検討してください。または、単にtextにして、無効な制限を削除します。

3

関数はVOLATILE(デフォルト)であるため、PostgreSQLはcentral.clienteのすべての行に対して同じ値を返すことを認識していないため、繰り返し評価されます。

ボラティリティをIMMUTABLEに設定すると、PostgreSQLはそれを1回だけ評価する必要があることを認識します。

ALTER FUNCTION ext.uf_converte_numerico(varchar(30)) IMMUTABLE;
3
Laurenz Albe

最初のケースでは、事前計算された値(v_cpf_numerico)を使用しています。

2番目のケースでは、選択中にcentral.clienteの各行の値が計算されます。

1
Nikolay Brel