web-dev-qa-db-ja.com

複数の配列を行にネスト解除

私は bulkinsertの優れた方法を示しています この例で:

WITH p AS (
    INSERT INTO parent_table (column_1) 
    SELECT $1 
    RETURNING id) 
INSERT INTO child_table (parent_table_id, column_a) 
SELECT p.id, a 
FROM   p, unnest($2::text[]) AS a

ただし、複数の配列から複数の行を挿入する必要があるため、次の構文を試しました。

WITH p AS (
    INSERT INTO parent_table (column_1) 
    SELECT $1 
    RETURNING id) 
INSERT INTO child_table (parent_table_id, column_a, column_b) 
SELECT p.id, a, b 
FROM   p, unnest($2::text[]) AS a, unnest($3::bigint[]) AS b

parent_table_idcolumn_aに主キーがあり、このクエリを実行しようとすると、Postgresが重複キー違反を報告します。

配列をラップ解除して個々の行を形成するにはどうすればよいですか?

言い換えると、$2$3の両方に2つのエントリがある場合、$2の最初のエントリは、$3の最初のエントリとのみ同じように挿入できます。それぞれの2番目のエントリ?

これが不可能な場合、多次元配列を作成できますか?もしそうなら、それは複数の配列型でどのように渡されるべきですか、そして多次元配列構文は何ですか?

7
user32234

これはあなたが望むことをするでしょう:

_WITH p AS (
    INSERT INTO parent_table (column_1) 
    SELECT $1 
    RETURNING id) 
INSERT INTO child_table (parent_table_id, column_a, column_b) 
SELECT p.id, t.a, t.b 
FROM   p, (SELECT unnest($2::text[]) AS a, unnest($3::bigint[]) AS b) t
_

ここでの微妙な違いは、同じSELECTリスト内の unnest() 呼び出しが、並列処理されている場合ネストされていない場合、要素のidenticalです。注意:ただし、Postgres 9.6以前では、数値が異なる場合、代わりにデカルト積になります。この動作はPostgres 10でサニタイズされました。見る:

generate_subscripts()または他の手法でよりクリーンなフォームを使用することもできますが、それらははるかに冗長になります。この関連質問の詳細:

Postgres 9.4

Postgres 9.4の新しい_WITH ORDINALITY_は、これに対してよりクリーンな(そして適度に冗長な)フォームを許可します:

_WITH p AS (...)
INSERT INTO child_table (...)
SELECT p.id, ta.a, tb.b 
FROM   p
     , unnest($2::text[]) WITH ORDINALITY AS ta(a, rn)
JOIN   unnest($3::bigint[]) WITH ORDINALITY AS tb(b, rn) USING (rn);
_

そして、この特別なケースは、複数の配列を受け入れるunnest()の新しい形式を使用して、さらに単純にすることができます

_WITH p AS (...)
INSERT INTO child_table (...)
SELECT p.id, t.a, t.b 
FROM   p, unnest($2::text[], $3::bigint[]) AS t(a, b);
_

この関連回答 の例。

11