Multiple INSERTS into one table and many to many table
You can do it all in one SQL command using CTEs.
Assuming Postgres 9.6 and this classic many-to-many schema (since you did not provide it):
CREATE TABLE questions ( question_id serial PRIMARY KEY, title text NOT NULL, body text, userid int, categoryid int);CREATE TABLE tags ( tag_id serial PRIMARY KEY, tag text NOT NULL UNIQUE);CREATE TABLE questiontags ( question_id int REFERENCES questions, tag_id int REFERENCES tags, PRIMARY KEY(question_id, tag_id));
To insert a single question with an array of tags:
WITH input_data(body, userid, title, categoryid, tags) AS ( VALUES (:title, :body, :userid, :tags) ) , input_tags AS ( -- fold duplicates SELECT DISTINCT tag FROM input_data, unnest(tags::text[]) tag ) , q AS ( -- insert question INSERT INTO questions (body, userid, title, categoryid) SELECT body, userid, title, categoryid FROM input_data RETURNING question_id ) , t AS ( -- insert tags INSERT INTO tags (tag) TABLE input_tags -- short for: SELECT * FROM input_tags ON CONFLICT (tag) DO NOTHING -- only new tags RETURNING tag_id )INSERT INTO questiontags (question_id, tag_id)SELECT q.question_id, t.tag_idFROM q, ( SELECT tag_id FROM t -- newly inserted UNION ALL SELECT tag_id FROM input_tags JOIN tags USING (tag) -- pre-existing ) t;
dbfiddle here
This creates any tags that do not exist yet on the fly.
The text representation of a Postgres array looks like this: {tag1, tag2, tag3}
.
If the input array is guaranteed to have distinct tags, you can remove DISTINCT
from the CTE input_tags
.
Detailed explanation:
- Insert data in 3 tables at a time using Postgres
- How to use RETURNING with ON CONFLICT in PostgreSQL?
- How to implement a many-to-many relationship in PostgreSQL?
- Cannot INSERT: ERROR: array value must start with "{" or dimension information
If you have concurrent writes you may have to do more. Consider the second link in particular.