sql >> Databáze >  >> RDS >> PostgreSQL

Proč PostgreSQL volá moji funkci STABLE/IMMUTABLE vícekrát?

Následující rozšíření vašeho testovacího kódu je informativní:

CREATE OR REPLACE FUNCTION test_multi_calls1(one integer)
RETURNS integer
AS $BODY$
BEGIN
    RAISE NOTICE 'Immutable called with %', one;
    RETURN one;
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE;
CREATE OR REPLACE FUNCTION test_multi_calls2(one integer)
RETURNS integer
AS $BODY$
BEGIN
    RAISE NOTICE 'Volatile called with %', one;
    RETURN one;
END;
$BODY$ LANGUAGE plpgsql VOLATILE;

WITH data AS
(
    SELECT 10 AS num
    UNION ALL SELECT 10
    UNION ALL SELECT 20
)
SELECT test_multi_calls1(num)
FROM data
where test_multi_calls2(40) = 40
and test_multi_calls1(30) = 30

VÝSTUP:

NOTICE:  Immutable called with 30
NOTICE:  Volatile called with 40
NOTICE:  Immutable called with 10
NOTICE:  Volatile called with 40
NOTICE:  Immutable called with 10
NOTICE:  Volatile called with 40
NOTICE:  Immutable called with 20

Zde můžeme vidět, že zatímco ve výběrovém seznamu byla neměnná funkce volána vícekrát, v klauzuli where byla volána jednou, zatímco volatile byla volána třikrát.

Důležité není, že PostgreSQL bude volat pouze STABLE nebo IMMUTABLE fungovat jednou se stejnými údaji – váš příklad jasně ukazuje, že tomu tak není – jde o to, že může volejte jen jednou. Nebo ji možná zavolá dvakrát, když by musel volat nestálou verzi 50krát, a tak dále.

Existují různé způsoby, jak lze využít stability a neměnnosti, s různými náklady a přínosy. Chcete-li poskytnout druh ukládání, který navrhujete, že by se měl provádět s výběrovými seznamy, musel by výsledky uložit do mezipaměti a poté vyhledat každý argument (nebo seznam argumentů) v této mezipaměti, než buď vrátí výsledek uložený v mezipaměti, nebo zavolá funkci na mezipaměti. -slečna, minout. To by bylo dražší než volání vaší funkce, a to i v případě, že by došlo k vysokému procentu zásahů do mezipaměti (může být 0 % přístupů do mezipaměti, což znamená, že tato „optimalizace“ odvedla práci navíc bez absolutně žádného zisku). Mohl by uložit možná jen poslední parametr a výsledek, ale to by zase mohlo být úplně zbytečné.

To platí zejména s ohledem na to, že stabilní a neměnné funkce jsou často nejlehčími funkcemi.

S klauzulí where však neměnnost test_multi_calls1 umožňuje PostgreSQL skutečně restrukturalizovat dotaz z prostého významu daného SQL:

Úplně na jiný plán dotazů:

Toto je druh využití, který PostgreSQL využívá STABLE a IMMUTABLE – nikoli ukládání výsledků do mezipaměti, ale přepisování dotazů na různé dotazy, které jsou efektivnější, ale poskytují stejné výsledky.

Všimněte si také, že test_multi_calls1(30) se volá před test_multi_calls2(40) bez ohledu na to, v jakém pořadí se objeví v klauzuli where. To znamená, že pokud první volání vede k tomu, že nejsou vráceny žádné řádky (nahraďte = 30 s = 31 otestovat), pak se volatile funkce vůbec nezavolá – opět bez ohledu na to, která je na které straně and .

Tento konkrétní druh přepisování závisí na neměnnosti nebo stabilitě. S where test_multi_calls1(30) != num k přepisování dotazu dojde u neměnných, ale nikoli pouze u stabilních funkcí. S where test_multi_calls1(num) != 30 k tomu vůbec nedojde (více hovorů), i když jsou možné další optimalizace:

Výrazy obsahující pouze funkce STABLE a IMMUTABLE lze použít s prohledáváním indexů. Výrazy obsahující VOLATILE funkce nemohou. Počet hovorů se může a nemusí snížit, ale mnohem důležitější je, že výsledky hovorů pak budou mnohem efektivněji využity ve zbytku dotazu (opravdu záleží jen na velkých tabulkách, ale pak to může udělat masivní rozdíl).

Celkově nepřemýšlejte o kategoriích volatility z hlediska memoizace, ale spíše z hlediska poskytování příležitostí plánovači dotazů PostgreSQL restrukturalizovat celé dotazy způsoby, které jsou logicky ekvivalentní (stejné výsledky), ale mnohem efektivnější.



  1. MySql.Data.MySqlClient.Replication.ReplicationManager vyvolá výjimku System.TypeInitializationException

  2. PostgreSQL Column neexistuje, ale ve skutečnosti existuje

  3. Rekurzivní dotaz v PostgreSQL. VYBRAT *

  4. Iterace přes celé číslo[] v PL/pgSQL