PostgreSQL nested INSERTs / WITHs for foreign key insertions
Don't nest the common table expressions, just write one after the other:
WITH Y AS (
INSERT INTO A (foo)
VALUES ('abc')
RETURNING id
), x as (
INSERT INTO B (a_id, bar)
SELECT id, 'def'
FROM Y
RETURNING id
)
INSERT INTO C (b_id, baz)
SELECT id, 'ghi'
FROM X;
How postgresql multiple insert works when there is foreign key constraint in the table itself?
Foreign key constraints are implemented with triggers in PostgreSQL. This is not directly documented, but you can see it indirectly here:
One can disable or enable a single trigger specified by name, or all triggers on the table, or only user triggers (this option excludes internally generated constraint triggers such as those that are used to implement foreign key constraints or deferrable uniqueness and exclusion constraints).
You can also see it with
SELECT * FROM pg_trigger
WHERE tgrelid = 'schools'::regclass;
The firing rules for triggers are documented and apply to foreign keys as well:
Row-level
BEFORE
triggers fire immediately before a particular row is operated on, while row-levelAFTER
triggers fire at the end of the statement (but before any statement-levelAFTER
triggers).
(Emphasis mine)
So foreign keys are validated after the complete statement is done.
Postgres insert value from insert in other table
You need a common table expression for this kind of insert chaining:
with ta as (
INSERT INTO tbl_b (status) VALUES ('OK')
RETURNING id
)
INSERT INTO tbl_a (name, tbl_b_reference)
VALUES ('myName', (select id from ta));
Another option is to simply use the lastval()
function to reference the last generated sequence value:
INSERT INTO tbl_b (status) VALUES ('OK');
INSERT INTO tbl_a (name, tbl_b_reference)
VALUES ('myName', lastval());
Note that you must not have any other statements that generate sequence values between those two.
Or use the currval() function:
INSERT INTO tbl_b (status) VALUES ('OK');
INSERT INTO tbl_a (name, tbl_b_reference)
VALUES ('myName', currval('tbl_b_id_seq'));
'tbl_b_id_seq'
is the standard name Postgres uses for a sequence that is created for a serial
column:
Insert a value from a table in another table as foreign key
You have two options.
The first one is using the lastval()
function which returns the value of the last generated sequence value:
insert into cinema(name, is_active) values ('Cinema One', true);
insert into theater(cinema_id) values (lastval());
Alternatively you can pass the sequence name to the currval()
function:
insert into theater(cinema_id)
values (currval(pg_get_serial_sequence('cinema', 'id')));
Alternatively you can chain the two statements using a CTE and the returning clause:
with new_cinema as (
insert into cinema (name, is_active)
values ('Cinema One', true)
returning id
)
insert into theater (cinema_id)
select id
from new_cinema;
In both statements I assume theater.id
is also a generated value.
PostgreSQL insert into multiple tables, using foreign key from first insertion in the second insertion
If you want to avoid problems with duplicates you should use a DISTINCT
clause in the first INSERT
. This will make two sections point to the same media_file but it should be more than okay. (Upgraded from @a_horse_with_no_name answer)
WITH ids AS (
INSERT INTO media_files (url, mimetype)
SELECT DISTINCT file_url, 'image/jpeg'
FROM sections
WHERE file_url IS NOT NULL
RETURNING id AS media_file_id, url AS file_url
)
INSERT INTO attachments (media_file_id, section_id)
SELECT ids.media_file_id, s.id
FROM ids
JOIN sections s ON s.file_url = ids.file_url;
Fiddle
PostgreSQL - Insert data into multiple tables simultaneously
The idea is to write WITH
clauses that contain INSERT ... RETRUNING
to return the generated keys. Then these “views for a single query” can be used to insert those keys into the referencing tables.
WITH par_key AS
(INSERT INTO participante (nome) VALUES ('Laurenz') RETURNING id),
ven_key AS
(INSERT INTO venda (inicio) VALUES (current_date) RETURNING id),
item_key AS
(INSERT INTO item (nome) VALUES ('thing') RETURNING id)
INSERT INTO lances_vendas (venda_id, item_id, participante_id, valor)
SELECT ven_key.id, item_key.id, par_key.id, numeric '3.1415'
FROM par_key, ven_key, item_key;
Can INSERT [...] ON CONFLICT be used for foreign key violations?
Yes, join your input rows to the referenced table, thereby removing rows without a match on the FK column:
INSERT INTO entries(entry_id, referenced_id, name)
SELECT val.entry_id, val.referenced_id, val.name
FROM (
VALUES (1, 2, 'references two')
-- more?
) val (entry_id, referenced_id, name)
JOIN referenced USING (referenced_id) -- drop rows without matching FK
ON CONFLICT (entry_id) DO NOTHING; -- drop rows with duplicate id
The UPSERT itself (INSERT ... ON CONFLICT DO NOTHING
) only reacts to unique violations. The manual:
ON CONFLICT
can be used to specify an alternative action to raising a unique constraint or exclusion constraint violation error. (See ON CONFLICT Clause below.)
Since the VALUES
expression is now not attached to an INSERT
directly, column types are not derived from the target table. You may need to cast input values explicitly when operating with non-basic types. See:
- Casting NULL type when updating multiple rows
Related Topics
Creating a Composite Foreign Key in SQL Server 2008
Oracle SQL - Sum and Group Data by Week
How to Specify in Clause in a Dynamic Query Using a Variable
Split String by Comma in SQL Server 2008
Is Order by Clause Allowed in a Subquery
SQL Server:Export Query as a .Txt File
How to Do Ms Access Database Paging + Search
Shows Blanks for Repeating Values in a Result Set
Postgres: Upgrade a User to Be a Superuser
Oracle Db: How to Write Query Ignoring Case
How to Find the Number of Occurrences of a Particular Character in a String Using SQL
SQL Unpivot Multiple Columns Data
SQL Server 2005 Recursive Query with Loops in Data - Is It Possible
Efficient Way to String Split Using Cte
Oracle Client and Networking Components Were Not Found