Följande förlängning av din testkod är 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
UTGÅNG:
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
Här kan vi se att medan den oföränderliga funktionen i select-listan anropades flera gånger, i where-satsen anropades den en gång, medan den flyktiga anropades tre gånger.
Det viktiga är inte att PostgreSQL bara kallar en STABLE
eller IMMUTABLE
fungera en gång med samma data - ditt exempel visar tydligt att så inte är fallet - det är att det kan ring det bara en gång. Eller kanske kommer den att kalla den två gånger när den skulle behöva anropa en flyktig version 50 gånger, och så vidare.
Det finns olika sätt på vilka stabilitet och oföränderlighet kan utnyttjas, med olika kostnader och fördelar. För att tillhandahålla den typ av besparing som du föreslår att den ska göra med urvalslistor måste den cachelagra resultaten och sedan slå upp varje argument (eller lista med argument) i denna cache innan den antingen returnerar det cachade resultatet eller anropar funktionen på en cache. -Fröken. Detta skulle vara dyrare än att anropa din funktion, även i de fall där det fanns en hög andel cacheträffar (det kan finnas 0 % cacheträffar vilket betyder att denna "optimering" gjorde extra arbete utan absolut ingen vinst). Det kan lagra kanske bara den sista parametern och resultatet, men det kan återigen vara helt värdelöst.
Detta är särskilt så med tanke på att stabila och oföränderliga funktioner ofta är de lättaste funktionerna.
Med where-satsen är dock oföränderligheten för test_multi_calls1
tillåter PostgreSQL att faktiskt omstrukturera frågan från den enkla betydelsen av den angivna SQL:
Till en helt annan frågeplan:
Det här är den sortens användning som PostgreSQL använder STABLE och IMUTABLE - inte cachning av resultat, utan omskrivning av frågor till olika frågor som är mer effektiva men ger samma resultat.
Observera också att test_multi_calls1(30) anropas före test_multi_calls2(40) oavsett vilken ordning de visas i where-satsen. Detta innebär att om det första anropet resulterar i att inga rader returneras (ersätt = 30
med = 31
för att testa) så kommer den flyktiga funktionen inte att anropas alls - igen oavsett vilken som finns på vilken sida av and
.
Denna speciella typ av omskrivning beror på oföränderlighet eller stabilitet. Med where test_multi_calls1(30) != num
Omskrivning av frågor kommer att ske för oföränderliga men inte för bara stabila funktioner. Med where test_multi_calls1(num) != 30
det kommer inte att hända alls (flera samtal) även om det finns andra optimeringar möjliga:
Uttryck som endast innehåller STABLE och IMUTABLE funktioner kan användas med indexskanningar. Uttryck som innehåller VOLATILE-funktioner kan inte. Antalet samtal kan eller kanske inte minskar, men mycket viktigare kommer resultaten av samtalen att användas på ett mycket mer effektivt sätt i resten av frågan (spelar bara roll på stora tabeller, men då kan det göra en massiv skillnad).
Tänk på det hela taget inte på volatilitetskategorier i termer av memoisering, utan snarare i termer av att ge PostgreSQL:s frågeplanerare möjligheter att omstrukturera hela frågor på sätt som är logiskt likvärdiga (samma resultat) men mycket effektivare.