web-dev-qa-db-ja.com

PL / pgSQLでレコードの複数の行を返す方法

私はRECORDデータ型を使用して複数のレコードを返そうとしていますが、RECORDに追加し、このRECORDに反復するたびに新しい値を追加/追加する方法があります。

つまり、recに追加して、ループが終了したときにrecが行のセットになるようにします。これは、関数の最後で返すだけです。現在、私はこれをやっています-

_SELECT temp_table.col1, temp_table.col2, temp_table.col3
      INTO rec
      FROM temp_table
      WHERE temp_table.col3 = false;
_

私の完全なコードはここにあります:

_CREATE OR REPLACE FUNCTION validation()
  RETURNS RECORD AS $$
DECLARE
        rec RECORD;
        temp_row RECORD;
BEGIN

  CREATE TEMPORARY TABLE temp_table (col1 TEXT, col2 INTEGER, col3 BOOLEAN) ON COMMIT DROP;

  FOR temp_row IN SELECT * FROM staging.validation
  LOOP

    RAISE NOTICE 'sql: %', temp_row.sql;

    EXECUTE format('INSERT INTO temp_table %s', temp_row.sql);

    IF (SELECT DISTINCT temp_table.col3 FROM temp_table WHERE temp_table.col3 = false)=false THEN
      RAISE NOTICE 'there is a false value';

      SELECT temp_table.col1, temp_table.col2, temp_table.col3
      INTO rec
      FROM temp_table
      WHERE temp_table.col3 = false;
    END IF;


  END LOOP;
  RETURN rec;
END; $$
LANGUAGE plpgsql;
_

SELECT validation();後の現在の出力

_validation
(crea_ddf,8095,f)
_

望ましい出力

_validation
(crea_ddf,8095,f)
(some_source_system,some_count,f)
(some_other_source_system,some_count,f)
(.....)
_
14
hky404

関数は、RECORDではなく_SETOF RECORD_を返す必要があり、次のように単一のRETURNではなく、行ごとに1つの_RETURN NEXT_が必要です。

_CREATE FUNCTION test() RETURNS SETOF RECORD AS $$
DECLARE
 rec record;
BEGIN
  select 1,2 into rec;
  return next rec;

  select 3,4 into rec;
  return next rec;
END $$ language plpgsql;
_

発信者:

 => select * from test()as x(a int、b int); 
 a | b 
 --- + --- 
 1 | 2 
 3 | 4 
(2行)
 

SQLは強く静的に型付けされているため、RECORD疑似型は扱いにくいことに注意してください。
多くの場合、最初から、各列の名前と型の完全な定義を備えた複合型を、無名型のTABLE(...)構文または_CREATE TYPE_は、永続的な名前付きタイプです。

14
Daniel Vérité

関数から複数のレコードを返す場合は、_setof record_および_return next rec_を使用します。例:

_create or replace function test_function()
    returns setof record 
    language plpgsql as $$
declare
    rec record;
begin
    for rec in
        select i, format('str%s', i), i/2*2 = i
        from generate_series(1, 3) i
    loop
        return next rec;
    end loop;
end $$;
_

このような関数は、列定義リストを使用してFROM句で呼び出す必要があります。

_select test_function(); -- NO

ERROR:  set-valued function called in context that cannot accept a set  

select * from test_function();  -- NO

ERROR:  a column definition list is required for functions returning "record"

select * from test_function() as (id int, str text, is_even boolean);

 id | str  | is_even 
----+------+---------
  1 | str1 | f
  2 | str2 | t
  3 | str3 | f
(3 rows)
_

より良いオプションは、returns table(...)および_return query_を使用することです。

_drop function if exists test_function();
create or replace function test_function()
    returns table (id int, str text, is_even boolean)
    language plpgsql as $$
begin
    return query
        select i, format('str%s', i), i/2*2 = i
        from generate_series(1, 3) i;
    -- you can use return query multiple times
    -- or assign values to columns
    -- and return the row:
    id = 100;
    str = 'extra';
    is_even = true;
    return next; -- without a parameter
end $$;
_

使用法:

_select test_function(); -- possible but rather impractical

 test_function 
---------------
 (1,str1,f)
 (2,str2,t)
 (3,str3,f)
 (100,extra,t)
(4 rows)

select * from test_function();

 id  |  str  | is_even 
-----+-------+---------
   1 | str1  | f
   2 | str2  | t
   3 | str3  | f
 100 | extra | t
(4 rows)
_
8
klin

これは危険信号です。

  1. テーブルvalidationがあります。
  2. 行を一時テーブルstagingに移動します。
  3. temp_table.col3 IS FALSEを含む行はすべてユーザーに返されます
  4. その列がfalseである指定されたテーブルのリスト内の他の行と一緒に。
  5. 次に、一時テーブルをドロップします(コミット時)

これをしてください.

WITH t AS ( SELECT true AS runthis FROM staging.validation WHERE col3 IS FALSE )
SELECT *
FROM staging.validation
WHERE t.runthis && col3 = 3
UNION ALL 
  SELECT *
  FROM some_source_system
  WHERE t.runthis && some_source_system.col3 = 3
UNION ALL 
  SELECT *
  FROM some_other_source_system
  WHERE t.runthis && some_other_source_system.col3 = 3;

必要に応じて、VIEWに配置することもできます

付記として

SELECT DISTINCT temp_table.col3
FROM temp_table
WHERE temp_table.col3 = false

ここでDISTINCTは何をしますか? LIMITを実行してください。実際、これはさらにクリーンだと主張します。

SELECT true
FROM temp_table
WHERE temp_table.col3 = false
LIMIT 1;

その後、奇妙な必要はありません= false ) = FALSE

1
Evan Carroll