2015-12-01 10 views
6

同じストアド・ファンクション内の別の問合せのレコード型変数に問合せ結果を格納するにはどうすればよいですか?私はPostgres 9.4.4を使用します。このようなテーブルでplpgsqlでレコード型変数を使用する方法は?

CREATE OR REPLACE FUNCTION func(_tbl regclass) 
RETURNS TABLE (t TEXT[], e TEXT[]) 
LANGUAGE plpgsql AS $$ 
DECLARE 
    t RECORD; 
    c INT; 
BEGIN 
    EXECUTE format('SELECT id, tags FROM %s', _tbl) INTO t; 
    SELECT count(*) FROM t INTO c; 
    RAISE NOTICE '% results', c; 
    SELECT * FROM t; 
END 
$$; 

...しかし、うまくいきませんでした:

create table test (id int, tags text[]); 
insert into test values (1,'{a,b,c}'), 
         (2,'{c,d,e}'); 

私は以下のように(単純化)関数を書いた

select func('test'); 
ERROR: 42P01: relation "t" does not exist 
LINE 1: SELECT count(*) FROM t 
          ^
QUERY: SELECT count(*) FROM t 
CONTEXT: PL/pgSQL function func(regclass) line 7 at SQL statement 
LOCATION: parserOpenTable, parse_relation.c:986 

答えて

15

コアの誤解:record変数は、単一の行(またはNULL)であり、テーブル(よく知られた型の0〜n行)ではありません。 PostgresまたはPL/pgSQLには「テーブル変数」はありません。があります。タスクに応じて、様々な選択肢があります:

したがって、あなたはrecord型変数に複数行を割り当てることはできません。この文では:

EXECUTE format('SELECT id, tags FROM %s', _tbl) INTO t; 

... Postgresは最初の行だけを割り当て、残りを捨てます。 「最初のもの」はあなたのクエリでうまく定義されていないので、最終的には任意の選択肢になります。明らかに、最初に述べた誤解のために。

record変数は、SQLクエリのテーブルの代わりに使用することもできません。それはあなたが得るエラーの主な原因です:

関係「t」は

存在しません。それは、count(*)t以来、そもそも何の意味も持たないだろうと、今では明らかですとにかく不可能であるだけでなく、単なるレコード/行です。

最後に(残りが機能する場合でも)戻り値の型が間違っているようです: (t TEXT[], e TEXT[]) tid, tagsを選択したので、(id int, e TEXT[])のようなものを返すことをおすすめします。あなたはこのように動作しますをやろうとしている何

CREATE OR REPLACE FUNCTION func(_tbl regclass) 
    RETURNS TABLE (id int, e text[]) AS 
$func$ 
DECLARE 
    _ct int; 
BEGIN 
    EXECUTE format(
     'CREATE TEMP TABLE tmp ON COMMIT DROP AS 
     SELECT id, tags FROM %s' 
    , _tbl); 

    GET DIAGNOSTICS _ct = ROW_COUNT; -- cheaper than another count(*) 

    -- ANALYZE tmp; -- if you are going to run multiple queries 

    RAISE NOTICE '% results', _ct; 

    RETURN QUERY TABLE tmp; 
END 
$func$ LANGUAGE plpgsql; 

コール(構文に注意してください!)

SELECT * FROM func('test'); 

関連:

概念のちょうど証拠。テーブル全体を選択している間は、基礎となるテーブルを代わりに使用します。実際にあなたが潜んでいるタイプの不一致の

慎重なクエリ内のいくつかのWHERE句...、count()戻りbigintがあるでしょう、あなたはinteger変数にそれを割り当てることができませんでした。キャストが必要でしょう:count(*)::int

しかし、私は完全に、それはEXECUTE後にこの権利を実行するために安価だと置き換え:

GET DIAGNOSTICS _ct = ROW_COUNT; 

Details in the manual.

なぜANALYZE?別に


:プレーンSQLでのCTEは、多くの場合、仕事を行うことができます。

+0

を一種のため、そんなにアーウィンをありがとうと詳細な答え。今やその理由は私にはとても分かります。私はその目的のためにCTEを使用します。 –

関連する問題