Mari kita membuat fungsi yang memiliki efek samping sehingga kita dapat melihat berapa kali dieksekusi:
CREATE OR REPLACE FUNCTION test.this_here(val integer)
RETURNS numeric
LANGUAGE plpgsql
AS $function$
BEGIN
RAISE WARNING 'I am called with %', val;
RETURN sqrt(val);
END;
$function$;
Dan sebut ini seperti yang Anda lakukan:
SELECT this_here(i) FROM generate_series(1,10) AS t(i) WHERE this_here(i) < 2;
WARNING: I am called with 1
WARNING: I am called with 1
WARNING: I am called with 2
WARNING: I am called with 2
WARNING: I am called with 3
WARNING: I am called with 3
WARNING: I am called with 4
WARNING: I am called with 5
WARNING: I am called with 6
WARNING: I am called with 7
WARNING: I am called with 8
WARNING: I am called with 9
WARNING: I am called with 10
this_here
──────────────────
1
1.4142135623731
1.73205080756888
(3 rows)
Seperti yang Anda lihat, fungsi dipanggil setidaknya sekali (dari WHERE
klausa), dan ketika kondisinya benar, sekali lagi untuk menghasilkan output.
Untuk menghindari eksekusi kedua, Anda dapat melakukan apa yang disarankan Edgar - yaitu membungkus kueri dan memfilter set hasil:
SELECT *
FROM (SELECT this_here(i) AS val FROM generate_series(1,10) AS t(i)) x
WHERE x.val < 2;
WARNING: I am called with 1
... every value only once ...
WARNING: I am called with 10
Untuk lebih lanjut memeriksa bagaimana ini bekerja, seseorang dapat pergi ke pg_stat_user_functions
dan memeriksa di calls
sana (diberikan track_functions
diatur ke 'semua).
Mari kita coba dengan sesuatu yang tidak memiliki efek samping:
CREATE OR REPLACE FUNCTION test.simple(val numeric)
RETURNS numeric
LANGUAGE sql
AS $function$
SELECT sqrt(val);
$function$;
SELECT simple(i) AS v
FROM generate_series(1,10) AS t(i)
WHERE simple(i) < 2;
-- output omitted
SELECT * FROM pg_stat_user_functions WHERE funcname = 'simple';
-- 0 rows
simple()
sebenarnya terlalu sederhana sehingga dapat diuraikan , oleh karena itu tidak muncul dalam tampilan. Mari kita membuatnya menjadi tidak dapat ditiru:
CREATE OR REPLACE FUNCTION test.other_one(val numeric)
RETURNS numeric
LANGUAGE sql
AS $function$
SELECT 1; -- to prevent inlining
SELECT sqrt(val);
$function$;
SELECT other_one(i) AS v
FROM generate_series(1,10) AS t(i)
WHERE other_one(i) < 2;
SELECT * FROM pg_stat_user_functions ;
funcid │ schemaname │ funcname │ calls │ total_time │ self_time
────────┼────────────┼───────────┼───────┼────────────┼───────────
124311 │ test │ other_one │ 13 │ 0.218 │ 0.218
SELECT *
FROM (SELECT other_one(i) AS v FROM generate_series(1,10) AS t(i)) x
WHERE v < 2;
SELECT * FROM pg_stat_user_functions ;
funcid │ schemaname │ funcname │ calls │ total_time │ self_time
────────┼────────────┼───────────┼───────┼────────────┼───────────
124311 │ test │ other_one │ 23 │ 0.293 │ 0.293
Seperti yang terlihat, gambarnya sama dengan atau tanpa efek samping.
Mengubah other_one()
ke IMMUTABLE
perubahan perilaku (mungkin mengejutkan) untuk lebih buruk, karena akan disebut 13 kali di kedua query.
STABLE
/IMMUTABLE
atauVOLATILE
?