Hãy tạo một hàm có tác dụng phụ để chúng ta có thể thấy nó được thực thi bao nhiêu lần:
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$;
Và sau đó gọi nó như bạn làm:
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)
Như bạn thấy, hàm được gọi ít nhất một lần (từ WHERE
mệnh đề) và khi điều kiện là đúng, một lần nữa để tạo đầu ra.
Để tránh thực hiện lần thứ hai, bạn có thể thực hiện những gì Edgar gợi ý - cụ thể là bọc truy vấn và lọc tập kết quả:
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
Để kiểm tra thêm cách thức hoạt động của nó, người ta có thể đi đến pg_stat_user_functions
và kiểm tra calls
ở đó (được cung cấptrack_functions
đặt thành 'tất cả).
Hãy thử với thứ gì đó không có tác dụng phụ:
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()
thực sự quá đơn giản nên có thể được nội tuyến , do đó nó không xuất hiện trong chế độ xem. Hãy làm cho nó không thể inlin:
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
Như vẻ ngoài của nó, hình ảnh là giống nhau có hoặc không có tác dụng phụ.
Thay đổi other_one()
để IMMUTABLE
thay đổi hành vi (có thể đáng ngạc nhiên) trở nên tồi tệ hơn, vì nó sẽ được gọi 13 lần trong cả hai truy vấn.
STABLE
/IMMUTABLE
hayVOLATILE
?