6

RECORD他のプログラミング言語で配列構造を使用してこれを行うことができるように、キー/インデックスによって型項目をループする必要があります。

例えば:

DECLARE
    data1    record;
    data2    text;
...
BEGIN
...
FOR data1 IN
    SELECT
        *
    FROM
        sometable
LOOP

    FOR data2 IN
        SELECT
            unnest( data1 )   -- THIS IS DOESN'T WORK!
    LOOP
        RETURN NEXT data1[data2];   -- SMTH LIKE THIS
    END LOOP;

END LOOP;
4

5 に答える 5

13

@Pavelが説明したように、配列をトラバースできるように、レコードをトラバースすることは単に可能ではありません。ただし、正確な要件に応じて、いくつかの方法があります。最終的には、すべての値を同じ列に返す必要があるため、それらを同じ型にキャストする必要がありtextます。すべての型にテキスト表現があるため、これは明らかな共通点です。

速くて汚い

integer、a、textおよび列を持つテーブルがあるとしdateます。

CREATE TEMP TABLE tbl(a int, b text, c date);
INSERT INTO tbl VALUES
 (1, '1text',     '2012-10-01')
,(2, '2text',     '2012-10-02')
,(3, ',3,ex,',    '2012-10-03')  -- text with commas
,(4, '",4,"ex,"', '2012-10-04')  -- text with commas and double quotes

次に、ソリューションは次のように簡単になります。

SELECT unnest(string_to_array(trim(t::text, '()'), ','))
FROM   tbl t;

最初の 2 行では機能しますが、3 行目と 4 行目の特殊なケースでは失敗し
ます。テキスト表現のカンマの問題は簡単に解決できます。

SELECT unnest(('{' || trim(t::text, '()') || '}')::text[])
FROM   tbl t
WHERE  a < 4;

これは問題なく動作します - テキスト表現に二重引用符がある 4 行目を除いて。それらは、それらを 2 倍にすることによってエスケープされます。ただし、配列コンストラクターでは、 でエスケープする必要があります\。なぜこの非互換性があるのか​​ わかりません...

SELECT ('{' || trim(t::text, '()') || '}') FROM tbl t WHERE a = 4

収量:

{4,""",4,""ex,""",2012-10-04}

ただし、次のものが必要です。

SELECT '{4,"\",4,\"ex,\"",2012-10-04}'::text[];  -- works

適切な解決策

事前に列名を知っていれば、きれいな解決策は簡単です。

SELECT unnest(ARRAY[a::text,b::text,c::text])
FROM tbl

既知のタイプのレコードを操作するため、システム カタログを照会するだけです。

SELECT string_agg(a.attname || '::text', ',' ORDER  BY a.attnum)
FROM   pg_catalog.pg_attribute a 
WHERE  a.attrelid = 'tbl'::regclass
AND    a.attnum > 0
AND    a.attisdropped = FALSE

これを動的 SQL を使用する関数に入れます。

CREATE OR REPLACE FUNCTION unnest_table(_tbl text)
  RETURNS SETOF text LANGUAGE plpgsql AS
$func$
BEGIN

RETURN QUERY EXECUTE '
SELECT unnest(ARRAY[' || (
    SELECT string_agg(a.attname || '::text', ',' ORDER  BY a.attnum)
    FROM   pg_catalog.pg_attribute a 
    WHERE  a.attrelid = _tbl::regclass
    AND    a.attnum > 0
    AND    a.attisdropped = false
    ) || '])
FROM   ' || _tbl::regclass;

END
$func$;

電話:

SELECT unnest_table('tbl') AS val

戻り値:

val
-----
1
1text
2012-10-01
2
2text
2012-10-02
3
,3,ex,
2012-10-03
4
",4,"ex,"
2012-10-04

これは、追加のモジュールをインストールしなくても機能します。別のオプションは、hstore拡張機能をインストールし、@ Craig が示すように使用することです。

于 2012-10-26T00:05:14.487 に答える
5

PL/pgSQL は、あなたがやりたいことのために設計されたものではありません。レコードが反復可能であるとは見なされません。それは、おそらく異なる、互換性のないデータ型のタプルです。

PL/pgSQL にEXECUTEは動的 SQL がありますが、EXECUTEクエリは PL/pgSQL 変数NEWや他のレコードを直接参照することはできません。

できることは、レコードをhstoreキー/値構造に変換してから、hstore. タプルeach(hstore(the_record))の行セットを生成する を使用します。key,valueすべての値は、そのtext表現にキャストされます。

ROW(..)このおもちゃの関数は、列名f1,を持つ匿名を作成し、それを に変換し、その列/値のペアを反復し、各ペアを返すことにより、レコードの反復をf2示しf3ますhstore

CREATE EXTENSION hstore;

CREATE OR REPLACE FUNCTION hs_demo()
RETURNS TABLE ("key" text, "value" text)
LANGUAGE plpgsql AS
$$
DECLARE
  data1 record;
  hs_row record;
BEGIN
  data1 = ROW(1, 2, 'test');
  FOR hs_row IN SELECT kv."key", kv."value" FROM each(hstore(data1)) kv
  LOOP
    "key" = hs_row."key";
    "value" = hs_row."value";
    RETURN NEXT;
  END LOOP;
END;
$$;

RETURN QUERY実際には、ループ全体を単純なステートメントに置き換えることができ、each(hstore)とにかく同じことを行うので、このように書くことは決してありませeach(hstore(record)).

于 2012-10-25T23:50:52.930 に答える
2

この機能は plpgsql ではサポートされていません - レコードは他のスクリプト言語のようにハッシュ配列ではありません - この機能が不可能な C や ADA に似ています。PLPerl や PLPython などの他の PL 言語、またはいくつかのトリックを使用できます。HSTORE データ型 (拡張) または動的 SQL を使用して反復処理できます。

動的 SQL を使用して複合変数フィールドの値を設定する方法を参照してください。

しかし、この機能の要求は通常意味するので、間違ったことをします。PL/pgSQL を使用する場合、Javascript や Python を使用する場合とは異なる考え方をする必要があります。

于 2012-10-25T16:13:33.090 に答える
0
FOR data2 IN
    SELECT d
    from  unnest( data1 ) s(d)
LOOP
    RETURN NEXT data2;
END LOOP;
于 2012-10-25T10:15:44.683 に答える
0

ループする前に結果を並べ替えると、目的を達成できますか。

for rc in select * from t1 order by t1.key asc loop
 return next rc;
end loop;

必要なことを正確に行います。また、その種のタスクを実行する最速の方法でもあります。

于 2012-10-25T12:45:18.097 に答える