Unnest mehrere Arrays in Zeilen

8

Mit diesem Beispiel wurde mir eine großartige Möglichkeit zum Bulkinsert gezeigt :

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

Ich muss jedoch mehrere Zeilen aus mehreren Arrays einfügen, daher habe ich diese Syntax ausprobiert:

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

Ich habe einen Primärschlüssel auf parent_table_idund column_a, und wenn ich versuche , diese Abfrage auszuführen, Postgres klagt über eine doppelte Schlüsselverletzung.

Wie sollen die Arrays entpackt werden, damit sie einzelne Zeilen bilden?

Mit anderen Worten, wenn $2und $3beide zwei Einträge haben, wie kann der erste Eintrag von $2nur mit dem ersten Eintrag von $3und derselbe für die jeweiligen zweiten Einträge eingefügt werden ?

Wenn dies nicht möglich ist, kann ich ein mehrdimensionales Array erstellen? Wenn ja, wie sollte es mit mehreren Array-Typen übergeben werden und wie lautet die mehrdimensionale Array-Syntax?

Gemeinschaft
quelle

Antworten:

12

Dies würde tun, was Sie wünschen:

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

Der subtile Unterschied besteht darin, dass unnest()Aufrufe in derselben SELECTListe nicht parallel verschachtelt sind, wenn die Anzahl der Elemente identisch ist . Achtung: Wenn in Postgres 9.6 oder älter die Anzahl nicht identisch ist, wird stattdessen ein kartesisches Produkt erstellt. Das Verhalten wurde in Postgres 10 bereinigt . Sehen:

Sie könnten eine sauberere Form mit generate_subscripts()oder anderen Techniken verwenden, aber diese wären viel ausführlicher. Details in dieser verwandten Frage:

Postgres 9.4

Das neue WITH ORDINALITYin Postgres 9.4 ermöglicht eine viel sauberere (und nur mäßig ausführlichere) Form dafür:

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);

Und dieser Sonderfall kann mit der neuen Form , die mehrere Arrays akzeptiert , noch einfacher seinunnest() :

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);

Beispiel in dieser verwandten Antwort .

Erwin Brandstetter
quelle
-1

Ich löste mit:

INSERT INTO foo(Val,i,j)
SELECT myArr[i][j] ,i,j 
FROM Generate_Series(1,lbound) i 
JOIN Generate_Series(1,ubound) j
    ON true
jurhas
quelle