ลองสร้างฟังก์ชั่นที่มีผลข้างเคียงเพื่อที่เราจะได้เห็นว่ามันถูกประมวลผลกี่ครั้ง:
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$;
จากนั้นเรียกสิ่งนี้เช่นคุณ:
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)
อย่างที่คุณเห็นฟังก์ชั่นนี้ถูกเรียกอย่างน้อยหนึ่งครั้ง (จากWHERE
clause) และเมื่อเงื่อนไขเป็นจริงอีกครั้งเพื่อสร้างเอาต์พุต
เพื่อหลีกเลี่ยงการประมวลผลครั้งที่สองคุณสามารถทำสิ่งที่เอ็ดการ์แนะนำ - คือห่อคำค้นและกรองชุดผลลัพธ์:
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
หากต้องการตรวจสอบเพิ่มเติมเกี่ยวกับวิธีการทำงานคุณสามารถไปที่pg_stat_user_functions
และตรวจสอบที่calls
นั่น (กำหนดให้track_functions
เป็น 'ทั้งหมด)
ลองด้วยสิ่งที่ไม่มีผลข้างเคียง:
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()
จริง ๆ แล้วง่ายเกินไปดังนั้นจึงสามารถถูกแทรกได้ดังนั้นจึงไม่ปรากฏในมุมมอง มาทำให้ไม่สามารถยกเลิกได้:
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
ตามที่ปรากฏรูปภาพจะเหมือนกันโดยมีหรือไม่มีผลข้างเคียง
การเปลี่ยนother_one()
เป็นการIMMUTABLE
เปลี่ยนแปลงพฤติกรรม (อาจเป็นเรื่องที่น่าประหลาดใจ) เป็นสิ่งที่แย่กว่านั้นเพราะมันจะถูกเรียก 13 ครั้งในข้อความค้นหาทั้งสอง
STABLE
/IMMUTABLE
หรือVOLATILE
?