क्वेरी को गति देने के लिए PL / pgSQL फ़ंक्शन के साथ एक रिकॉर्ड लौटाएं


10

मेरे पास पर्ल में लिखित एक नॉन- फोर्किंग गेम डेमॉन है , जो एक पोस्टग्रेक्यूएल 9.3 डेटाबेस में खिलाड़ी आँकड़े लिखने के लिए एक्यूनिक प्रश्नों का उपयोग करता है। लेकिन जब मुझे डेटाबेस से कुछ पढ़ने की ज़रूरत होती है (जैसे कि यदि किसी खिलाड़ी पर प्रतिबंध है या यदि खिलाड़ी के पास वीआईपी स्थिति है), तो मैं तुल्यकालिक प्रश्नों का उपयोग करता हूं।

यह तब तक खेल को कम समय के लिए रोक देता है, जब तक कि डेटाबेस से मूल्य को पढ़ा नहीं जाता है।

मैं पढ़ने के मूल्यों के लिए async प्रश्नों का उपयोग करने के लिए अपने खेल डेमॉन को फिर से नहीं लिख सकता (मैंने कोशिश की, लेकिन इसमें बहुत सारे बदलावों की आवश्यकता थी), इसलिए मेरा सवाल है : क्या यह कई असंबंधित प्रश्नों को संयोजित करने के लिए समझ में आएगा (जब मुझे एक नया खिलाड़ी बनाना होगा कनेक्ट करता है) 1 प्रक्रिया के लिए और मैं अपने पर्ल प्रोग्राम में एक ही समय में कई मान कैसे लौटा सकता हूं?

मेरे वर्तमान प्रश्न सभी एक खिलाड़ी आईडी को पैरामीटर के रूप में लेते हैं और 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या TRUEमेरे में is_bannedइस कथन से चर: select true into is_banned from pref_ban where id=_id। वहाँ करने के लिए इसे बदलने के लिए एक रास्ता है FALSEया TRUE?
अलेक्जेंडर फारबर

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मापदंडों का उपयोग मूल रूप से एक ही चीज़ को प्राप्त करता है, लेकिन उपयोगकर्ता-परिभाषित प्रकारों को
बनाए

@pozs +1 धन्यवाद, मैं OUTमापदंडों का उपयोग करना चाहूंगा - लेकिन SELECT4 असंबंधित प्रश्नों के मामले में उन्हें कैसे ?
अलेक्जेंडर फारबर

@klin +1 धन्यवाद, मैंने आपके सुझाव की कोशिश की है और यह काम करता है। अपने कस्टम प्रकार को बनाने के लिए मैंने उपयोग किया है drop type if exists user_type cascade; create type user_type as(...);क्योंकि मेरी पर्ल स्क्रिप्ट हर बार स्टार्ट अप समय पर एसक्यूएल स्टेटमेंट को कॉल करती है।
अलेक्जेंडर फारबर

1
यदि आप ऐसा नहीं करना चाहिए। Postgres में कार्य संग्रहीत कार्यविधियाँ हैं। एक बार बनने के बाद किसी भी सत्र में उपयोग करने के लिए तैयार हैं। उपयोगकर्ता द्वारा परिभाषित समान चिंताएं। आपको केवल एक कंपोजिट प्रकार छोड़ना होगा यदि आप इसे बदलने जा रहे हैं (या इसे बिल्कुल हटा दें)।
क्लिन

My_function () से "चयन करें" के लिए +1। मैं "my_function ()" का चयन कर रहा था और परेशानी हो रही थी।
इलोनपिलाजा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.