Trả về một bản ghi với chức năng PL / pgSQL - để tăng tốc truy vấn


10

Tôi có một trình nền trò chơi không giả mạo được viết bằng Perl , sử dụng truy vấn acync để ghi số liệu thống kê của người chơi vào cơ sở dữ liệu PostgreQuery 9.3. Nhưng khi tôi cần đọc thứ gì đó từ cơ sở dữ liệu (như nếu người chơi bị cấm hoặc nếu người chơi có trạng thái VIP), thì tôi sử dụng truy vấn đồng bộ.

Điều này làm cho trò chơi dừng lại trong một thời gian ngắn, cho đến khi giá trị đã được đọc từ cơ sở dữ liệu.

Tôi không thể viết lại trình nền trò chơi của mình để sử dụng các truy vấn không đồng bộ để đọc các giá trị (tôi đã thử, nhưng nó yêu cầu quá nhiều thay đổi), vì vậy câu hỏi của tôi là : có nên kết hợp một số truy vấn không liên quan (mà tôi cần thực hiện khi có người chơi mới kết nối) với 1 thủ tục và làm cách nào tôi có thể trả lại nhiều giá trị cùng lúc cho chương trình Perl của mình?

Tất cả các truy vấn hiện tại của tôi đều lấy ID người chơi làm tham số và trả về 1 giá trị:

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

Để kết hợp các truy vấn trên có lẽ tôi cần một quy trình như thế này:

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;

Xin hãy giúp tôi khai báo đúng quy trình trên.

Câu trả lời:


13

Về cơ bản, việc sử dụng OUTcác tham số đạt được điều tương tự như trong câu trả lời của @ klin, nhưng không tạo ra các loại do người dùng xác định. Chỉ cần di chuyển tất cả các biến của bạn từ khối khai báo vào danh sách đối số làm OUTtham số:

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;

Điều này sẽ trả về một bản ghi (chính xác là một), vì vậy bạn có thể chọn các giá trị của nó như một bản ghi bình thường:

-- 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 này hoạt động rất tốt, cảm ơn. Chỉ cần một câu hỏi nhỏ: hiện tại tôi có NULLhoặc TRUEtrong is_bannedbiến của mình với câu lệnh này : select true into is_banned from pref_ban where id=_id. Có cách nào để thay đổi nó thành FALSEhay TRUE?
Alexander Farber

1
Vâng, is_banned := exists(select 1 from pref_ban where id=_id)nên làm việc, nhưng đó là một câu hỏi khác nhau.
pozs

6

Bạn nên xác định một loại hỗn hợp. Bạn có thể sử dụng nó như kiểu trả về của hàm và cho các biến bản ghi bên trong hàm.

Thí dụ:

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

Theo tôi, việc sử dụng các hàm như thế này là khá hợp lý về cả hiệu năng và logic ứng dụng.


Các kiểu hỗn hợp do người dùng định nghĩa là rất hữu ích nếu bạn muốn trả về tập hợp các hàng từ hàm của bạn. Sau đó, bạn nên xác định kiểu trả về của hàm setof composite-typevà sử dụng return nexthoặcreturn query.

Thí dụ:

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
OUTVề cơ bản, việc sử dụng các tham số đạt được điều tương tự, nhưng không tạo ra các loại do người dùng định nghĩa: postgresql.org/docs/civerse/static/
ám

@pozs +1 cảm ơn, tôi muốn sử dụng các OUTtham số - nhưng làm thế nào SELECTchúng trong 4 trường hợp truy vấn không liên quan của tôi?
Alexander Farber

@klin +1 cảm ơn, tôi đã thử đề xuất của bạn và nó hoạt động. Để tạo loại tùy chỉnh của tôi, tôi đã sử dụng drop type if exists user_type cascade; create type user_type as(...);vì tập lệnh Perl của tôi gọi các câu lệnh SQL mỗi lần khởi động.
Alexander Farber

1
Bạn không nên làm điều đó. Các chức năng trong Postgres được lưu trữ thủ tục. Sau khi tạo xong đã sẵn sàng để sử dụng trong bất kỳ phiên nào. Những mối quan tâm giống nhau do người dùng xác định. Bạn phải loại bỏ một kiểu hỗn hợp chỉ khi bạn sẽ thay đổi nó (hoặc loại bỏ nó).
klin

+1 cho "chọn * từ my_feft ()". Tôi đã thực hiện "select my_feft ()" và gặp sự cố.
Ilonpilaaja 16/03/19
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.