sql >> Database teknologi >  >> RDS >> PostgreSQL

Hvorfor kalder PostgreSQL min STABLE/IMMUTABLE funktion flere gange?

Følgende udvidelse af din testkode er informativ:

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

OUTPUT:

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

Her kan vi se, at mens den uforanderlige funktion i select-listen blev kaldt flere gange, i where-sætningen blev den kaldt én gang, mens den flygtige blev kaldt tre gange.

Det vigtige er ikke, at PostgreSQL kun kalder en STABLE eller IMMUTABLE fungere én gang med de samme data - dit eksempel viser tydeligt, at dette ikke er tilfældet - det er, at det kan kalder det kun én gang. Eller måske vil den kalde den to gange, når den skulle kalde en flygtig version 50 gange, og så videre.

Der er forskellige måder, hvorpå stabilitet og uforanderlighed kan udnyttes, med forskellige omkostninger og fordele. For at give den slags lagring, du foreslår, at den skal foretage med select-lister, skal den cache resultaterne og derefter slå hvert argument (eller listen over argumenter) i denne cache, før enten det cachelagrede resultat returneres eller kaldes funktionen på en cache. -gå glip af. Dette ville være dyrere end at kalde din funktion, selv i det tilfælde, hvor der var en høj procentdel af cache-hits (der kunne være 0% cache-hits, hvilket betyder, at denne "optimering" gjorde ekstra arbejde uden absolut ingen gevinst). Det kunne måske kun gemme den sidste parameter og resultatet, men det kunne igen være fuldstændig ubrugeligt.

Dette er især i betragtning af, at stabile og uforanderlige funktioner ofte er de letteste funktioner.

Med where-sætningen dog uforanderligheden af ​​test_multi_calls1 tillader PostgreSQL faktisk at omstrukturere forespørgslen fra den almindelige betydning af den angivne SQL:

Til en helt anden forespørgselsplan:

Dette er den slags brug, som PostgreSQL gør af STABLE og IMUTABLE - ikke caching af resultater, men omskrivning af forespørgsler til forskellige forespørgsler, som er mere effektive, men giver de samme resultater.

Bemærk også, at test_multi_calls1(30) kaldes før test_multi_calls2(40), uanset hvilken rækkefølge de optræder i where-sætningen. Dette betyder, at hvis det første opkald resulterer i, at der ikke returneres rækker (erstat = 30 med = 31 for at teste), så kaldes den flygtige funktion slet ikke - igen uanset hvilken side af and .

Denne særlige form for omskrivning afhænger af uforanderlighed eller stabilitet. Med where test_multi_calls1(30) != num omskrivning af forespørgsler vil ske for uforanderlige, men ikke for kun stabile funktioner. Med where test_multi_calls1(num) != 30 det vil ikke ske overhovedet (flere opkald), selvom der er andre optimeringer mulige:

Udtryk, der kun indeholder STABLE og IMUTABLE funktioner, kan bruges med indeksscanninger. Udtryk, der indeholder VOLATILE-funktioner, kan ikke. Antallet af opkald kan falde eller ikke falde, men meget vigtigere vil resultaterne af opkaldene så blive brugt på en meget mere effektiv måde i resten af ​​forespørgslen (det betyder kun virkelig noget på store borde, men så kan det gøre en massiv forskel).

Tænk i det hele taget ikke på volatilitetskategorier i form af memoisering, men snarere i forhold til at give PostgreSQL's forespørgselsplanlægger muligheder for at omstrukturere hele forespørgsler på måder, der er logisk ækvivalente (samme resultater), men meget mere effektive.



  1. SQL Server Cursor Reference (syntaks osv.)

  2. Django + MySQL på Mac OS 10.6.2 Snow Leopard

  3. Sådan opdaterer du eksisterende data med SQLite

  4. oracle sql-forespørgsel for at vise alle datoerne for den foregående måned