PL / pgSQL işlevli bir kayıt döndürme - sorguyu hızlandırmak için


10

Ben Perl ile yazılmış olmayan bir çatallama oyun cini bir PostgreSQL 9.3 veritabanına yazma oyuncu istatistiklerine sorguları acync kullanır. Ama veritabanından bir şey okumam gerektiğinde (bir oyuncunun yasaklanması veya oyuncunun VIP statüsü olması gibi), o zaman senkronize sorgular kullanırım.

Bu, değer veritabanından okunana kadar oyunu kısa bir süre durdurur.

Değerleri okumak için zaman uyumsuz sorguları kullanmak için oyun arka plan programımı yeniden yazamıyorum (denedim, ancak çok fazla değişiklik gerektirdi), bu yüzden sorum şu : birkaç alakasız sorguyu birleştirmek mantıklı mı (yeni bir oyuncu olduğunda yapmam gerekir) bağlanır) ve 1 Perl programına aynı anda birkaç değeri nasıl döndürebilirim?

Geçerli sorgularımın tümü parametre olarak bir oyuncu kimliği alır ve 1 değeri döndürür:

-- 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=?

Yukarıdaki sorguları birleştirmek için muhtemelen böyle bir prosedüre ihtiyacım var:

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;

Lütfen yukarıdaki prosedürü doğru bir şekilde beyan etmeme yardımcı olun.

Yanıtlar:


13

OUTParametreleri kullanmak temelde @ klin'in cevabındakiyle aynı şeyi elde eder, ancak kullanıcı tanımlı tipler oluşturmadan. Tüm değişkenlerinizi declare bloğundan OUTparametre olarak argüman listesine taşıyın :

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;

Bu, bir kaydı (tam olarak bir) döndürür, böylece değerlerini normal bir kayıt olarak seçebilirsiniz:

-- 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 bu harika çalışıyor, teşekkürler. Sadece bir küçük soru: Şu anda ben de var NULLya TRUEbenim de is_bannedbu ifadeye değişkeni: select true into is_banned from pref_ban where id=_id. Bunu değiştirmek için bir yolu var mı FALSEyoksa TRUE?
Alexander Farber

1
Evet, is_banned := exists(select 1 from pref_ban where id=_id)işe yaramalı, ama bu farklı bir soru.
Haziran'da pozlar

6

Kompozit tip tanımlamalısınız . Geri dönüş işlevi olarak ve bir işlev içindeki kayıt değişkenleri için kullanabilirsiniz.

Misal:

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();

Bence bu tür fonksiyonları kullanmak hem performans hem de uygulama mantığı açısından oldukça makul.


İşlevinizden satır kümesi döndürmek istiyorsanız, kullanıcı tanımlı kompozit türleri çok kullanışlıdır. Sonra işlevin dönüş türünü tanımlamalı setof composite-typeve return nextveyareturn query.

Misal:

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
OUTParametreleri kullanmak temelde aynı şeyi
başarır

@pozs +1 teşekkürler, OUTparametreleri kullanmak istiyorum - ama SELECT4 alakasız sorgu benim durumumda nasıl ?
Alexander Farber

@klin +1 teşekkürler, önerinizi denedim ve işe yarıyor. Özel tipimi oluşturmak için kullandığım drop type if exists user_type cascade; create type user_type as(...);için Perl betiğim her seferinde SQL deyimlerini çağırıyor.
Alexander Farber

1
Bunu yapmamalısın. Postgres'deki işlevler saklı yordamlardır. Oluşturulduktan sonra herhangi bir oturumda kullanıma hazırdır. Aynı şey kullanıcı tanımlı tipler için de geçerlidir. Bir kompozit türü yalnızca değiştirecek (veya kaldıracaksanız) bırakmalısınız.
klin

+1 "my_function () 'dan * seçin" için. "Select my_function ()" yapıyordum ve sorun yaşıyordum.
Ilonpilaaja
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.