web-dev-qa-db-ja.com

複合型の配列を関数パラメーターとして使用してアクセスする

Postgresに2つのBooksフィールドと2つのnumericフィールドを持つタイプvarcharを作成しました。 Booksの配列を関数に送信して、一致するテーブルのそれらの値をINSERTに送信します。

これは私のタイプです:

CREATE TYPE Books AS (
V_Book_ID NUMERIC,
V_Row_Num NUMERIC,
V_Book_OWNER TEXT,
V_Book_OWNER_ID TEXT
);

これは私の機能です:

CREATE OR REPLACE FUNCTION Update_Table(row_book Books[]) RETURNS TEXT AS $$
DECLARE
   Status TEXT;
   I_Max integer := array_length(row_book, 1);
BEGIN
FOR I in 1..I_Max
  LOOP
   INSERT INTO books_table(Book_ID,
   Row_Num,
   Book_OWNER,
   Book_OWNER_ID)
   values
   (row_book[I].V_Book_ID,
   row_book[I].V_Row_Num,
   row_book[I].V_Book_OWNER,
   row_book[I].V_Book_OWNER_ID);
END LOOP;

   STATUS:='Saved';
exception when others then
   STATUS:='failure';
   RETURN STATUS;

END;
$$ language plpgsql;

関数にデータを送信するにはどうすればよいですか、またはデータで関数を呼び出すにはどうすればよいですか?

4

私はSO少し前に基本的に同じ質問に答えました:

@a_horseと同じように、unnest()を使用して同じソリューションを提案します。

仮定して books_tableは複合タイプbooksと同じ行タイプです。追加のタイプを作成する必要はまったくありません。テーブルの行タイプを使用するだけです。

CREATE TABLE books_table (
  book_id numeric
, row_num numeric
, book_owner text
, book_owner_id text
);

PL/pgSQL関数

宣言されていない理由でplpgsql関数が必要な場合、これを検討してください。

CREATE OR REPLACE FUNCTION update_table_variadic(VARIADIC _books_arr books_table[])
  RETURNS TEXT AS
$func$
DECLARE
   b books_table;
BEGIN
   FOREACH b IN ARRAY _books_arr
   LOOP
      INSERT INTO books_table  -- rare case where column list is not necessary
      SELECT b.*;
   END LOOP;

   RETURN 'Saved';

EXCEPTION WHEN others THEN
   RETURN 'Failure';
END
$func$ LANGUAGE plpgsql;

呼び出し(行の値のリストを使用して!):

SELECT update_table_variadic('(2,100,Arthur,1)', '(2,50,Zaphod,1)');

VARIADICを使用しない場合、関数呼び出しには全体として単一の配列が必要になります。 配列リテラルオプションで明示的にキャスト):

SELECT update_table('{"(1,100,Arthur,1)","(1,50,Zaphod,1)"}'::books_table[]);

または、配列コンストラクター@ a_horseの例 のように使用できます。多くの場合、配列リテラルの方が簡単です。

主なポイント:

  • より単純なFOREACHを使用して配列をループします。

  • 何をしているのかわからない場合は、PostgresでCaMeLケース名を使用しないでください。

  • (オプションで)VARIADICパラメーターを使用して、関数呼び出しの構文を簡略化します。このようにして、行の値のlistを提供できます。 VARIADICを使用する場合は、関数パラメーターの最大数(デフォルトは100)に注意してください。

SQL関数

例外をキャッチする必要がなく、文字列 'Saved'/'Failure'を返す必要がない場合は、単純化します。

CREATE OR REPLACE FUNCTION update_table_set(VARIADIC _books_arr books_table[])
  RETURNS void AS
$func$
   INSERT INTO books_table
   SELECT * FROM unnest(_books_arr) b;
$func$ LANGUAGE sql;

SQLフィドル

7

タイプの配列を作成するには、明示的な配列コンストラクターを使用します。

array[(1,100,'Arthur',1), (1,50,'Zaphod',1)]::books[]

したがって、関数を呼び出すには、以下を使用する必要があります。

select update_table(array[(1,100,'Arthur',1), (1,50,'Zaphod',1)]::books[])

ただし、関数にはエラーがあります。ループの後、returnステートメントがありません。例外ブロックのステートメントはonly実行されているためですif例外が発生します。

したがって、次のようなものが必要です。

begin 

  .... 

  STATUS:='Saved';
  return status;  ---<<< this is missing

  exception when others then
    STATUS:='failure';
    RETURN STATUS; --<<< this is only execute if an exception occurs

END;

または、別のbegin .. end;ブロックが必要です。

begin 

  begin    
    for ... 
    end loop;

    STATUS:='Saved';

  exception when others then
    STATUS:='failure';
  end;

  RETURN STATUS; 
END;

無関係ですが、配列を反復するループは必要ありません。これは、1つのステートメントを使用してより効率的に行うことができます。

INSERT INTO books_table 
   (Book_ID, Row_Num, Book_OWNER, Book_OWNER_ID)
select *
from unnest(row_book);