簡體   English   中英

在存儲函數中使用預准備語句

[英]Using prepared statement in stored function

我在數據庫中有一個表:

create table store (
    ...
    n_status        integer not null,
    t_tag           varchar(4)
    t_name          varchar,
    t_description   varchar,
    dt_modified     timestamp not null,
    ...
);

在我的存儲函數中,我需要多次對該表執行相同的select

select * from store
where n_place_id = [different values]
and t_tag is not null
and n_status > 0
and (t_name ~* t_search or t_description ~* t_search)
order by dt_modified desc
limit n_max;

這里, t_searchn_max是存儲函數的參數。 我認為為此使用准備好的聲明是有意義的,但我遇到了奇怪的問題。 這就是我所擁有的:

create or replace function fn_get_data(t_search varchar, n_max integer)
  returns setof store as
$body$
declare
    resulter        store%rowtype;
    mid             integer;
begin
    prepare statement prep_stmt(integer) as
        select *
          from store
         where n_place_id = $1
           and (t_name ~* t_search or t_description ~* t_search)
      order by dt_modified
         limit n_max;

    for mid in
        (select n_place_id from ... where ...)
    loop
        for resulter in
            execute prep_stmt(mid)
        loop
            return next resulter;
        end loop;
    end loop;
end;$body$
  language 'plpgsql' volatile;

但是,當我實際運行該功能時

select * from fn_get_data('', 30)

我收到此錯誤:

ERROR:  column "t_search" does not exist
LINE 3:   and (t_name ~* t_search or t_description ~* t_search)
                         ^
QUERY:  prepare prep_stmt(integer) as
        select * from store where n_status > 0 and t_tag is not null and n_museum = $1
        and (t_name ~* t_search or t_description ~* t_search)
        order by dt_modified desc limit maxres_free

好吧,也許它不喜歡准備好的語句中的外部變量,所以我把它改成了

prepare prep_stmt(integer, varchar, integer) as
select * from store where n_status > 0 and t_tag is not null and n_museum = $1
and (t_name ~* $2 or t_description ~* $2)
order by dt_modified desc limit $3

...

for resulter in
    execute prep_stmt(mid, t_search, n_max)

...

這次我得到一個不同的錯誤:

ERROR:  function prep_stmt(integer, character varying, integer) does not exist
LINE 1: SELECT prep_stmt(mid, t_search, n_max)
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
QUERY:  SELECT prep_stmt(mid, t_search, n_max)

我在這里錯過了什么?

編輯我在頂部添加了相關的表結構。

在我看來,像動態SQL的PL / PgSQL EXECUTE勝過准備語句的常規SQL EXECUTE

碼:

create or replace function prep_test() returns void as $$
begin
    PREPARE do_something AS SELECT 1;
    EXECUTE do_something;
end;
$$ LANGUAGE 'plpgsql';

測試:

regress=# select prep_test(1);
ERROR:  column "do_something" does not exist
LINE 1: SELECT do_something
               ^
QUERY:  SELECT do_something
CONTEXT:  PL/pgSQL function "prep_test" line 4 at EXECUTE statement

在PL / PgSQL之外它工作正常:

regress=# EXECUTE do_something;
?column?
----------
        1
(1 row)

我不確定你是如何在PL / PgSQL中執行准備好的語句的。

出於興趣, 為什么要嘗試在PL / PgSQL中使用預處理語句? 無論如何,為PL / PgSQL准備和緩存計划,它會自動發生。

有一種方法可以在函數中EXECUTE預處理語句,但是就像接受的答案所說的那樣,你通常不會在函數中執行此操作,因為函數已經存儲了它的計划。

話雖如此,仍然存在需要在函數中使用預准備語句的用例。 我的用例是當為不同的用戶使用多個模式時,模式包含類似命名的表,並且您希望使用相同的函數根據search_path設置的內容訪問其中一個表。 在這種情況下,由於函數存儲其計划的方式,在更改search_path之后使用相同的函數會導致事務中斷。 我已經說過,這個問題有兩個解決方案。 第一種是在這里使用EXECUTE '<Your query as a string here>' 但是對於大型查詢來說這可能會非常難看,因此使用第二種方法的原因是涉及PREPARE

因此,關於'為什么'你想要這樣做的背景,這里是如何:

CREATE OR REPLACE FUNCTION prep_test()
  RETURNS void AS $$
BEGIN
  PREPARE do_something AS SELECT 1;
  EXECUTE 'EXECUTE do_something;';
END;
$$ LANGUAGE plpgsql;

盡管添加一些保護措施以防止其破壞可能符合您的最佳利益。 就像是:

CREATE OR REPLACE FUNCTION prep_test()
  RETURNS void AS $$
BEGIN
  IF (SELECT count(*) FROM pg_prepared_statements WHERE name ilike 'do_something') > 0 THEN
    DEALLOCATE do_something;
  END IF;

  PREPARE do_something AS SELECT 1;
  EXECUTE 'EXECUTE do_something;';

  DEALLOCATE do_something;
END;
$$ LANGUAGE plpgsql;

同樣,那些認為他們想要這樣做的人,通常可能不應該這樣做,但對於那些需要它的情況,這就是你如何做到的。

您可以在PLPGSQL中使用這樣的EXECUTE語句:

select magicvalue into str_execute from magicvalues where magickey = ar_requestData[2];
EXECUTE str_execute into str_label USING ar_requestData[3], ar_requestData[4]::boolean, ar_requestData[5]::int, ar_requestData[6];

這是我在我的應用程序中使用的代碼。 ar_requestData是一個包含文本值的數組。 在表magicvalues我存儲像准備語句之類的東西。 select語句例如:

insert into classtypes(label, usenow, ranking, description) values($1,$2,$3,$4) returning label'

親切的問候,

Loek Bergman

plpgsql中不允許使用PREPARE語句。 您可以拼接函數內的所有語句,然后使用動態執行。 這是一個例子。

create or replace function sp_test(f_total int) returns void as $ytt$
declare v_sql text;
declare i int;
begin
  v_sql:='prepare ytt_s1 (int,timestamp) as select * from tbl1 where id = $1 and log_time = $2;';
  while i < f_total
  loop
    v_sql:=v_sql||'execute ytt_s1('||i||',now());';
    i := i + 1;
  end loop;
  v_sql:=v_sql||'deallocate ytt_s1;';
  execute v_sql;
end;
$ytt$ language plpgsql;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM