PL / pgSQL 함수로 레코드 반환-쿼리 속도 향상


10

나는이 Perl로 작성 비 분기 게임 데몬 PostgreSQL의 9.3 데이터베이스에 쓰기 플레이어 통계에 쿼리 acync 사용합니다. 그러나 데이터베이스에서 무언가를 읽을 필요가있을 때 (플레이어가 금지되었거나 플레이어가 VIP 상태 인 경우) 동기 쿼리를 사용합니다.

데이터베이스에서 값을 읽을 때까지 게임이 잠시 중단됩니다.

값을 읽기 위해 비동기 쿼리를 사용하기 위해 게임 데몬을 다시 작성할 수 없습니다 (시도했지만 너무 많은 변경이 필요했습니다). 내 질문은 : 관련되지 않은 여러 쿼리를 결합하는 것이 합리적입니까? (새로운 플레이어를 만들 때 만들어야 함) 연결))를 1 절차로 바꾸고 어떻게 여러 값을 동시에 Perl 프로그램에 반환 할 수 있습니까?

현재 쿼리는 모두 플레이어 ID를 매개 변수로 사용하고 1 값을 반환합니다.

-- Has the player been banned?
select true from pref_ban where id=?

-- What is the reputation of this player?
select
count(nullif(nice, false)) -
count(nullif(nice, true)) as rep
from pref_rep where id=?

-- Is he or she a special VIP player?
select vip > now() as vip from pref_users where id=?

-- How many games has the player played to the end?
select completed from pref_match where id=?

위의 쿼리를 결합하려면 다음과 같은 절차가 필요할 것입니다.

create or replace function get_user_info(_id varchar) returns XXX as $BODY$
    declare
        is_banned boolean;
        reputation integer;
        is_vip boolean;
        completed_games integer;
    begin

        select 1 into is_banned from pref_ban where id=_id;

        select
        count(nullif(nice, false)) -
        count(nullif(nice, true)) 
        into reputation
        from pref_rep where id=_id;

        select vip > now() into is_vip from pref_users where id=_id;

        select completed into completed_games from pref_match where id=_id;

        return XXX; /* How to return 4 values here? */

    end;
$BODY$ language plpgsql;

위 절차를 올바르게 선언하도록 도와주세요.

답변:


13

OUT매개 변수를 사용하면 기본적으로 @klin의 답변과 동일하지만 사용자 정의 유형을 만들지 않습니다. 선언 블록의 모든 변수를 OUT매개 변수 로 인수 목록으로 이동하십시오 .

create or replace function get_user_info(
    IN  _id varchar,
    OUT is_banned boolean,
    OUT reputation integer,
    OUT is_vip boolean,
    OUT completed_games integer
)
-- no returns clause necessary, output structure controlled by OUT parameters
-- returns XXX
as $BODY$
begin
    select true into is_banned from pref_ban where id=_id;

    select
    count(nullif(nice, false)) -
    count(nullif(nice, true)) 
    into reputation
    from pref_rep where id=_id;

    select vip > now() into is_vip from pref_users where id=_id;

    select completed into completed_games from pref_match where id=_id;

    -- no return statement necessary, output values already stored in OUT parameters
    -- return XXX;
end
$BODY$ language plpgsql;

그러면 레코드 (정확히 하나)가 반환되므로 해당 값을 일반 레코드로 선택할 수 있습니다.

-- this will return all properties (columns) from your function:
select * from get_user_info();

-- these will return one property (column) from your function:
select is_banned from get_user_info();
select (get_user_info()).is_banned;

+1 감사합니다. 단 하나의 작은 질문 : 현재이 문장으로 변수에 NULL또는 TRUEis_banned변수가 select true into is_banned from pref_ban where id=_id있습니다. FALSE또는 로 변경하는 방법이 TRUE있습니까?
Alexander Farber

1
예, is_banned := exists(select 1 from pref_ban where id=_id)작동해야하지만 다른 질문입니다.
pozs

6

복합 유형을 정의해야합니다 . 함수의 리턴 유형 및 함수 내의 레코드 변수로 사용할 수 있습니다.

예:

create type user_type as (
    is_banned boolean,
    reputation integer,
    is_vip boolean,
    completed_games integer);

create or replace function check_user_type ()
returns user_type language plpgsql as $$
declare
    rec user_type;
begin
    select true into rec.is_banned;
    select 100 into rec.reputation;
    select false into rec.is_vip;
    select 22 into rec.completed_games;
--  you can do the same in a little bit nicer way:
--  select true, 100, false, 22 into rec
    return rec;
end $$;

select * from check_user_type();

필자의 의견으로는 이와 같은 기능을 사용하는 것은 성능과 응용 프로그램 논리 측면에서 상당히 합리적입니다.


사용자 정의 복합 유형은 함수에서 행 집합을 반환하려는 경우 매우 유용합니다. 그런 다음 함수의 반환 유형을 정의 setof composite-type하고 return next또는return query.

예:

create or replace function check_set_of_user_type ()
returns setof user_type language plpgsql as $$
declare
    rec user_type;
begin
    for rec in
        select i/2*2 = i, i, i < 3, i+ 20
        from generate_series(1, 4) i
    loop
        return next rec;
    end loop;

    return query 
        select true, 100+ i, true, 100+ i
        from generate_series(1, 2) i;
end $$;

select * from check_set_of_user_type();

 is_banned | reputation | is_vip | completed_games
-----------+------------+--------+-----------------
 f         |          1 | t      |              21
 t         |          2 | t      |              22
 f         |          3 | f      |              23
 t         |          4 | f      |              24
 t         |        101 | t      |             101
 t         |        102 | t      |             102

1
OUT매개 변수를 사용하면 기본적으로 동일한 작업을 수행하지만 사용자 정의 형식을 만들지 않아도됩니다. postgresql.org/docs/current/static/…
pozs

@ pozs +1 덕분에 OUT매개 변수 를 사용하고 싶습니다. 하지만 SELECT4 개의 관련없는 쿼리의 경우 어떻게 됩니까?
Alexander Farber

@ klin +1 덕분에, 제안을 시도해 보았습니다. 내 사용자 정의 유형을 만들기 위해 drop type if exists user_type cascade; create type user_type as(...);Perl 스크립트가 시작할 때마다 SQL 문을 호출하기 때문에 사용 했습니다.
Alexander Farber

1
그렇게하지 말아야합니다. Postgres의 함수는 저장 프로 시저입니다. 생성되면 모든 세션에서 사용할 수 있습니다. 사용자 정의 형식도 마찬가지입니다. 복합 유형을 변경하거나 완전히 제거하려는 경우에만 복합 유형을 삭제해야합니다.
클라인

"select * from my_function ()"에 +1 "select my_function ()"을하고 있었고 문제가 발생했습니다.
Ilonpilaaja
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.