Insert inserted id to another table - sql

Here's the scenario:
create table a (
id serial primary key,
val text
);
create table b (
id serial primary key,
a_id integer references a(id)
);
create rule a_inserted as on insert to a do also insert into b (a_id) values (new.id);
I'm trying to create a record in b referencing to a on insertion to a table. But what I get is that new.id is null, as it's automatically generated from a sequence. I also tried a trigger AFTER insert FOR EACH ROW, but result was the same. Any way to work this out?

To keep it simple, you could also just use a data-modifying CTE (and no trigger or rule):
WITH ins_a AS (
INSERT INTO a(val)
VALUES ('foo')
RETURNING a_id
)
INSERT INTO b(a_id)
SELECT a_id
FROM ins_a
RETURNING b.*; -- last line optional if you need the values in return
Related answer with more details:
PostgreSQL multi INSERT...RETURNING with multiple columns
Or you can work with currval() and lastval():
How to get the serial id just after inserting a row?
Reference value of serial column in another column during same INSERT

Avoid rules, as they'll come back to bite you.
Use an after trigger on table a that runs for each row. It should look something like this (untested):
create function a_ins() returns trigger as $$
begin
insert into b (a_id) values (new.id);
return null;
end;
$$ language plpgsql;
create trigger a_ins after insert on a
for each row execute procedure a_ins();

Don't use triggers or other database Kung fu. This situation happens every moment somewhere in the world - there is a simple solution:
After the insertion, use the LASTVAL() function, which returns the value of the last sequence that was auto-incremented.
Your code would look like:
insert into a (val) values ('foo');
insert into b (a_id, val) values (lastval(), 'bar');
Easy to read, maintain and understand.

Related

How to insert a newly generated id into another table with a trigger in postgresql?

Basically, users when they create a new record in mytable1, there is an id field that needs to be the same across multiple tables. I achieve this by having mytable2 with the s_id as primary key
My current function looks like
CREATE OR REPLACE FUNCTION test.new_record()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
case when new.s_id in (select s_id from mytable1) then
insert into mytable2 (sprn, date_created) select max(s_id) +1, now() from mytable2 ;
update mytable1 set new.s_id = (select max(b.s_id) from mytable2 b);
end case;
RETURN new;
END;
$function$;
Intended was when the s_id is replicated then it would create a new entry on mytable2. This new entry would then be updated onto mytable1
Problem with this function is that right now it does not recognise the new on the update part of the function.
How to keep the s_id take the value on every new insert ?
If you want to have one "generator" across multiple tables, create one sequence that is used across all those tables for the default value:
create sequence the_id_sequence;
create table one
(
id integer primary key default nextval('the_id_sequence')
.... other columns
);
create table two
(
id integer primary key default nextval('the_id_sequence')
.... other columns ...
);
If you want to replicate an ID from one table to another during insert, you only need one sequence:
create table one
(
-- using identity is the preferred over "serial" to auto-generate PK values
id integer primary key generated always as identity
);
create table two
(
id integer primary key
);
create or replace function insert_two()
returns trigger
as
$$
begin
insert into two (id) values (new.id);
return new;
end;
$$
language plpgsql;
create trigger replicate_id
before insert on one
for each row
execute procedure insert_two();
Then if you run:
insert into one (id) values (default);
A row with exactly the same id value will be inserted into table two.
If you don't have a generated ID column so far, use the following syntax:
alter table one
add testidcolumn bigint generated always as identity;

Insert Into Oracle Table with single, autoincrement Column

Imagine the following (fictional) situation:
You have a table with only one column id that is the primary key, autoincremented by using a typical sequence + trigger combination.
How would you create a new row there as you have to specify the values keyword for the insert query?
INSERT INTO table () VALUES () is not valid as far as I understood.
ATTENTION:
This is not a discussion about the sense of such a table! It is out of pure technical interest.
In any current Oracle version (12.1, 12.2, 18) I would not use a trigger but an identity column - then use the default keyword during insert:
create table x (id integer generated by default as identity);
insert into x (id) values (default);
How about
INSERT INTO theTable (id) VALUES (null);
and your before insert trigger would be like:
if :NEW.id is NULL Then
SELECT id_sequence.NEXTVAL INTO :NEW.id FROM dual;
end if;

Tackling nested inserts using functions

Hi people i need some help deciding on the best way to do an insert into table ‘shop’ which has a serial id field. I also need to insert into tables ‘shopbranch’ and ‘shopproperties’ which both references shop.id.
In a nutshell I need to insert one shop record. Then two records for each table of the following tables, shopproperty and shopbranch, whose shopid (FK) references the just created shop.id field
I saw somewhere that i could wrap the ‘shop’ insert, inside a function called lets say ‘insert_shop’ which does the 'shop' insert and returns its id using a select statement
Then inside another function which inserts shoproperty and shopbranch records i could do one call to insert_shop function to return the shop id which can be used to be passed in as the shop id for the records.
Can you let me know if I’m looking at this in the correct way as I’m a newbie.
One way to approach this is to create a view on your three tables that shows all columns from all three tables that can be inserted or updated. If you then create an INSTEAD OF INSERT trigger on the view then you can manipulate the view contents as if it were a table. You can do the same with UPDATE and even combine the two into an INSTEAD OF INSERT OR UPDATE trigger. The function that your trigger calls then has three INSERT statements that redirect the insert on the view to the underlying tables:
CREATE TABLE shop (
id serial PRIMARY KEY,
nm text,
...
);
CREATE TABLE shopbranch (
id serial PRIMARY KEY,
shop integer NOT NULL REFERENCES shop,
branchcode text,
loc text,
...
);
CREATE TABLE shopproperties (
id serial PRIMARY KEY,
shop integer NOT NULL REFERENCES shop,
prop1 text,
prop2 text,
...
);
CREATE VIEW shopdetails AS
SELECT s.*, b.*, p.*
FROM shop s, shopbranch b, shopproperties p,
WHERE b.shop = s.id AND p.shop = s.id;
CREATE FUNCTION shopdetails_insert() RETURNS trigger AS $$
DECLARE
shopid integer;
BEGIN
INSERT INTO shop (nm, ...) VALUES (NEW.nm, ...) RETURNING id INTO shopid;
IF NOT FOUND
RETURN NULL;
END;
INSERT INTO shopbranch (shop, branchcode, loc, ...) VALUES (shopid, NEW.branchcode, NEW.loc, ...);
INSERT INTO shopproperties(shop, prop1, prop2, ...) VALUES (shopid, NEW.prop1, NEW.prop2, ...);
RETURN NEW;
END; $$ LANGUAGE plpgsql;
CREATE TRIGGER shopdetails_trigger_insert
INSTEAD OF INSERT
FOR EACH ROW EXECUTE PROCEDURE shopdetails_insert();
You could of course play with the view and show only those columns from the three tables that can be inserted or updated (such as excluding primary and foreign keys).

In PostgreSQL how do you insert into a table with only one identity column?

For instance:
{create table Participant ( id serial, primary key(id) );}
How do you insert into table in this case?
If you create the table like above,
You can use default in following way to insert:
INSERT INTO Participant values(default);
Check out SQLFIDDLE.
Another way to insert is:
INSERT INTO Participant values(NEXTVAL('Participant_id_seq'));
CREATE TABLE will create implicit sequence "Participant_id_seq" for serial column "Participant.id".
You can get the sequence for the table using pg_get_serial_sequence function in following way:
pg_get_serial_sequence('Participant', 'id')
It will take new value from sequence using NEXTVAL().
Check out SQLFIDDLE
insert into Participant values (default);

How do I insert into a table and get back the primary key value?

I have a primary key set up to auto increment.
I am doing multiple queries and I need to retrieve that primary key value to use as a foreign key in another table (IsIdentity = TRUE).
Is there any elegant way to get back the primary key value when I do an insert query? Right now I am requerying and getting the highest value in that column which seems really hacky.
Any suggestions?
If you are using SQL Server 2005 or later, you can use the OUTPUT clause.
create table T(
pk int identity primary key,
dat varchar(20)
);
go
insert into T
output inserted.pk
values ('new item');
go
drop table T;
The output can be directed to a table as well as to the client. For example:
create table T(
pk int identity primary key,
dat varchar(20)
);
create table U(
i int identity(1001,1) primary key,
T_pk int not null,
d datetime
);
go
insert into T
output inserted.pk, getdate()
into U(T_pk,d)
values ('new item'), ('newer item');
go
select * from T;
select * from U;
go
drop table T, U;
Beginning with SQL Server 2008, you can use "composable DML" for more possibilities.
insert into YourTable values (...)
get the new PK with scope_identity()
select scope_identity()
INSERT INTO YourTable (1, 2, etc.)
OUTPUT inserted.yourIDcolumn
VALUES (value1, value2, value...)
Note: This is for MS SQL 2005 and greater
SCOPE_IDENTITY() is probably what you want. It returns the ID of the last record inserted by the same code context in which it executes.
IDENT_CURRENT('tablename') is subject to concurrency issues. That is, there's no guarantee that another record won't be inserted between the INSERT and the call to IDENT_CURRENT.
I must confess, I'm not sure to what source of amazement the VillageIdiot's outburst refers, but I myself am quite astonished that this question does not appear to be a duplicate at all.
holy crap!!!
just call SCOPE_IDENTITY() function:
insert into your_talble(col1,col2) values('blah','more blah')
select scope_identity()
because selecting highest value will return error if any other statement make an insert. the function scope_identity() returns the identity created in current context (that is by your statement)
You should use scope_identity(). And I recommend to wrap insert statement and scope_identity() into transaction.