Reputation: 5815
I'd like to insert multiple (2) rows into a table in Postgres, and use the ids returned by WITH ROWS AS
in an insert to another table (columns id_1
and id_2
in second_table
), and return that id as the final result of the query.
first_table
is the main data collection, and second_table
essentially just links two first_table
entries together.
Here's the gist of what I'm trying to do:
WITH row AS (
INSERT INTO first_table (some_data, more_data)
VALUES (DEFAULT, DEFAULT), (DEFAULT, DEFAULT)
RETURNING first_table_id
)
INSERT INTO second_table (id_1, id_2, other_data)
VALUES (???, ???, DEFAULT)
RETURNING second_table_id
I am using Node.js and the node-pg
module; I know that this can be done in Node.js by having the first query return the two rows returned by WITH ROWS AS
, then running another prepared statement using the two ids for the next insert.
I'm not sure if I can do this through a trigger, since ON INSERT
only has one row. Maybe it would have two since these rows are inserted into first_table
in the same transaction, but I don't have extensive experience with Postgres/SQL in general so I'm not sure how I could do this via trigger.
The problem I'm having is that the WITH ROWS AS
statement returns 2 rows (intended, obviously), but I can't figure out how to access each row independently in the next insert, and I have a feeling that it isn't possible.
Can this be accomplished in a single query, or do I have to use a trigger or Node.js for this?
Upvotes: 2
Views: 3309
Reputation: 36274
The second insert needs to be an INSERT INTO ... SELECT ... FROM first_cte
. If you can make an assumtion, that the second generated id is larger than the first (or at least, distinct & comparable -- this is the case, if you use PostgreSQL's serial
, or directly a sequence), you can use aggregation:
WITH rows AS (
INSERT INTO first_table (some_data, more_data)
VALUES (DEFAULT, DEFAULT), (DEFAULT, DEFAULT)
RETURNING first_table_id
)
INSERT INTO second_table (id_1, id_2)
SELECT min(first_table_id), max(first_table_id)
FROM rows
RETURNING second_table_id
Note that, other_data
is omitted: you cannot use the DEFAULT
in INSERT INTO ... SELECT ...
.
For a more general solution, you can use arrays:
WITH rows AS (
INSERT INTO first_table (some_data, more_data)
VALUES (DEFAULT, DEFAULT), (DEFAULT, DEFAULT)
RETURNING first_table_id
)
INSERT INTO second_table (id_1, id_2, other_data)
SELECT first_table_ids[1], first_table_ids[2], 5 -- value '5' for "other_data"
FROM (
SELECT array_agg(first_table_id) AS first_table_ids
FROM rows
) AS rows_as_array
RETURNING second_table_id
Upvotes: 3