私は Perlで記述された非分岐ゲームデーモン を持っています。これは、acyncクエリを使用して、プレーヤーの統計情報をPostgreSQL 9.3データベースに書き込みます。しかし、データベースから何かを読み取る必要がある場合(プレーヤーが禁止されている場合や、プレーヤーに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;
上記の手順を適切に宣言するのを手伝ってください。
OUT
parameters を使用すると、@ 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;
これはレコード(正確には1つ)を返すため、その値を通常のレコードとして選択できます。
-- 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;
複合型を定義する必要があります。 関数の戻り型として、および関数内のレコード変数に使用できます。
例:
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