11

データベースにテーブルがあります。

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_searchおよびn_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)

ここで何が欠けていますか?

編集関連するテーブル構造を上部に追加しました。

4

4 に答える 4

9

EXECUTE動的SQLのPL/PgSQLはEXECUTE、プリペアドステートメントの通常のSQLよりも優れているように見えます。

コード:

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用に準備され、キャッシュされます。これは自動的に行われます。

于 2012-10-03T12:04:04.367 に答える
4

関数でプリペアドステートメントを作成する方法はありますがEXECUTE、受け入れられた答えが言ったように、関数はすでに計画を格納しているため、通常、関数でこれを実行する必要はありません。

そうは言っても、関数でプリペアドステートメントを使用する必要があるユースケースはまだあります。これの私の使用例は、異なるユーザーに複数のスキーマを使用する場合です。スキーマには同様の名前のテーブルが含まれており、同じ関数を使用して、search_path設定内容に基づいてこれらのテーブルの1つにアクセスします。この場合、関数がプランを保存する方法がsearch_path原因で、変更後に同じ関数を使用すると、問題が発生します。私が述べたこの問題には2つの解決策があります。1つ目はを使用することEXECUTE '<Your query as a string here>'です。ただし、これは大規模なクエリでは非常に醜くなる可能性があるため、を含む2番目の方法を使用する理由があります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;

繰り返しになりますが、これをやりたいと思っている人は、通常はそうすべきではありませんが、それが必要な場合は、これがあなたのやり方です。

于 2015-01-17T01:17:22.027 に答える
2

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

于 2012-10-04T09:26:30.517 に答える
1

PREPAREステートメントはplpgsql内では許可されていません。関数内のすべてのステートメントをつなぎ合わせて、その後動的実行を使用できます。これが例です。

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;
于 2014-01-13T03:13:46.057 に答える