将多个数组取消嵌套到行中

8 postgresql array set-returning-functions bulk-insert

我已经看到了使用此示例进行批量插入的好方法

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
Run Code Online (Sandbox Code Playgroud)

但是,我需要从多个数组中插入多行,所以我尝试了以下语法:

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
Run Code Online (Sandbox Code Playgroud)

我在parent_table_idand上有一个主键column_a,当我尝试执行此查询时,Postgres 抱怨重复键冲突。

应该如何解开数组以便它们形成单独的行?

换句话说,如果$2$3都有两个条目,那么第一个条目如何$2只插入第一个条目,$3并且对于各自的第二个条目都相同?

如果这是不可能的,我可以构造一个多维数组吗?如果是这样,它应该如何与多种数组类型一起传递,多维数组语法是什么?

Erw*_*ter 12

这会做你想要的:

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
Run Code Online (Sandbox Code Playgroud)

这里的微妙差别的是,unnest()在相同的调用SELECT列表中嵌套的并行如果要素的数量是相同的。但要小心:在 Postgres 9.6 或更早版本中,如果数字相同,则会生成笛卡尔积。该行为在Postgres 10 中进行了消毒。看:

您可以使用更简洁的形式 withgenerate_subscripts()或其他技术,但这些会更加冗长。此相关问题中的详细信息:

Postgres 9.4

WITH ORDINALITYPostgres 9.4 中的新功能允许使用更简洁(并且只是稍微冗长)的形式:

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);
Run Code Online (Sandbox Code Playgroud)

使用接受多个数组的新形式,这种特殊情况甚至可以更简单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);
Run Code Online (Sandbox Code Playgroud)

此相关答案中的示例。