SELECT 및 WHERE 절에서 동일한 기능


11

초보자 질문 :

f(x, y)내 데이터베이스 테이블의 두 열 x 및 y에 비싼 기능이 있습니다.

함수의 결과를 열로 제공하고 그것에 제약을 두는 쿼리를 실행하고 싶습니다.

SELECT *, f(x, y) AS func FROM table_name WHERE func < 10;

그러나 이것은 작동하지 않으므로 다음과 같은 것을 작성해야합니다.

SELECT *, f(x, y) AS func FROM table_name WHERE f(x, y) < 10;

고가의 기능을 두 번 실행합니까? 가장 좋은 방법은 무엇입니까?


1
함수가 STABLE/ IMMUTABLEVOLATILE?
Evan Carroll

답변:


22

몇 번 실행되는지 볼 수 있도록 부작용이있는 함수를 만들어 봅시다 :

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절 에서 한 번 이상 호출되고 조건이 true이면 다시 한 번 출력을 생성합니다.

두 번째 실행을 피하기 위해 Edgar가 제안한대로 , 즉 쿼리를 래핑하고 결과 집합을 필터링 할 수 있습니다.

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것이 'all').

부작용이없는 것으로 시도해 보자.

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 번 호출 될 것이다 변경 악화로 동작을 (어쩌면 당연한).


함수를 다시 호출하기로 한 결정은 함수 본문에 부작용 명령이 있는지에 따라 결정될 수 있습니까? 쿼리 계획을보고 동일한 매개 변수를 가진 함수가 행당 한 번 또는 여러 번 호출되는지 여부를 확인할 수 있습니까 (예 : 부작용이없는 경우)?
Andriy M

@AndriyM 나는 그렇다고 상상할 수 있지만 현재 실제로 호출되는 것을보기 위해 디버거를 사용할 시간이 없습니다. 인라인 함수에 대해 약간 추가합니다 (OP가 들릴 때 OP가 기대하는 것은 아닙니다).
dezso

1
@AndriyM, postgresql.org/docs/9.1/static/sql-createfunction.html 에 따르면 함수는 IMMUTABLE 또는 STABLE로 선언되지 않은 경우 VOLATILE로 간주됩니다. VOLATILE은 단일 테이블 스캔에서도 함수 값이 변경 될 수 있으므로 최적화를 수행 할 수 없음을 나타냅니다.
Lennart

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.