How to insert a record and return the primary key to update the foreign key in another table? - sql

I need to add value to a foreign key column in a table (table1). For this I have to create a new record in another table (table2) and return the handle to update the column with foreign key in the first table (table1).
Also, when I insert the new record in table2, I need a value contained in table1 for one of the columns in table2.
UPDATE table1
SET table2_id = (INSERT INTO table2 (id, anumber, atimestamp, atext)
VALUES (nextval('seqtable2'), 0, NOW()::TIMESTAMP, table1.anumber::TEXT)
RETURNING id );
I believe that with the above script (even not working) it is possible to understand the problem. I wrote in the simplest and most summarized way.
I'm looking for a solution for PostgreSQL 9.4, but if there are alternatives to later versions, I'd like to know as well.
Thank you very much in advance.

You can use pg/psql as follows:
DO
$updatecode$
DECLARE
i int;
BEGIN
INSERT INTO table2 (id, anumber, atimestamp, atext)
VALUES (nextval('seqtable2'), 0, NOW()::TIMESTAMP, 2::TEXT)
RETURNING id into i;
UPDATE table1 SET table2_id = i WHERE ...
END;
$updatecode$
LANGUAGE plpgsql;
Edit:
I had to add a loop because I needed to change multiple records. Another change was to be able to use a value of table1 in the atext column while inserting the record into table2.
DO
$insertforupdate$
DECLARE
tbl1 table1%ROWTYPE;
tbl2_id INTEGER;
BEGIN
FOR tbl1 IN SELECT * FROM table1
LOOP
INSERT INTO table2 (id, anumber, atimestamp, atext)
VALUES (nextval('seqtable2'), 0, NOW()::TIMESTAMP, tbl1.anumber::TEXT)
RETURNING id INTO tbl2_id;
UPDATE table1 SET table2_id = tbl2_id WHERE id = tbl1.id;
END LOOP;
END;
$insertforupdate$
LANGUAGE plpgsql;

Related

How do I create this trigger?

I have two tables: table1 and table2. I have a trigger in table1 that inserts the current row into table2 based on some conditions. If the row gets inserted into table2, I want to delete that row from table1. Now in Oracle, it seems we cannot delete the current row from the trigger in table1 itself.
A row level trigger on a table can manipulate the data of the updated rows. It cannot perform additional dml on the table itself (select, insert, delete).
A possible solution is to create a view on table1 with an INSTEAD OF trigger that deletes from table1 and perform all insert/update/delete statements on the view instead of the table
Example: when test_table.name is updated to 'KOEN', then row itself will be deleted. This example shows just an ON UPDATE trigger but it can be done for INSERT/UPDATE/DELETE:
CREATE TABLE test_table (
id NUMBER
GENERATED BY DEFAULT ON NULL AS IDENTITY
CONSTRAINT test_table_id_pk PRIMARY KEY,
name VARCHAR2(100 CHAR)
);
CREATE VIEW test_table_v AS
SELECT
id,
name
FROM
test_table;
CREATE OR REPLACE TRIGGER test_table_v_bu
INSTEAD OF UPDATE ON test_table_v
FOR EACH ROW
DECLARE
BEGIN
-- do your stuff on the other table.
IF :NEW.NAME = 'KOEN' THEN
DELETE FROM test_table WHERE id = :NEW.ID;
END IF;
END;
/
koen>INSERT INTO test_table ( name ) VALUES ( 'JIM' );
1 row inserted.
koen>select * from test_table;
ID NAME
_____ _______
3 JIM
koen>update test_table_v set name= 'KOEN';
1 row updated.
stapp_dev--SAMPLEAPPS>select * from test_table;
no rows selected
koen>

Updating another table on conflict postgres

I have a requirement to insert on one table (test01) and update on another table(result) whenever the conflict arises. I tried with below query:
insert into test01 as cst (col1,col2)
select col1,col2 from (
select 1 col1,'test' col2) as excluded
on conflict (col1) do
update result as rst set conflictid = excluded.col1, updated_at = now() where rst.conflictid= excluded.col1 ;
but its returns "syntax error at or near result". Can anyone please help me with the right solution.
Basically, your approach is not feasible. The ON CONFLICT ... DO UPDATE clause applies only to the table into which the rows are inserted. See INSERT syntax in the documentation.
A solution requires a bit more work. You should create a trigger for table test01 to get the effect you want.
Example tables (slightly changed names of columns and table):
create table test01_conflicts(
conflict_id int primary key,
updated_at timestamp);
create table test01(
id int primary key,
str text);
When the table test01 is updated with the same id the trigger function inserts or updates a row in the conflict table. Note that in this case the function returns null so the update on test01 will not proceed.
create or replace function before_update_on_test01()
returns trigger language plpgsql as $$
begin
if new.id = old.id then
insert into test01_conflicts(conflict_id, updated_at)
values (new.id, now())
on conflict(conflict_id)
do update set updated_at = now();
return null;
end if;
return new;
end $$;
create trigger before_update_on_test01
before update on test01
for each row execute procedure before_update_on_test01();
The query. On conflict update test01 with the same id only:
insert into test01
select col1, col2
from (
select 1 as col1, 'test' as col2
) as source
on conflict (id) do
update set
id = excluded.id;

After Update Trigger SQL

Hey guys i created two tables in oracle sql, first one has 2 columns, and the second has 3 columns(one of them is a foreign key from Pk of first table).
I want to create a trigger that AFTER i update the column of the foreign key in second table, will update the other columns according to the value of the pk.
Table1(idF, name)
table2(id, idF, name)
I want to create a trigger that when i update idF(foreign key) in table2 will display the same name as in table1.
You can create trigger in oracle as follows:
Create or replace trigger trg_table2
Before update of idf on table2
For each row
When (old.idf <> new.idf and new.idf is not null)
Begin
Select name into :new.name
from table1
Where idf = :new.idf;
End;
/
you can use this after update trigger on table1
delimiter $$
create trigger MyTrigger after update on table1
for each row
begin
update table2 set name = new.name where idF = new.idF;
end$$

Comparing :new value inserted with a trigger

I'm trying to build a trigger that checks if the row that is gonna be inserted, exists in another table.
Basically my 2 tables share one column, ID.
I want to prevent the insertion when the new row doesnt exist at least once in the other table.
I have this:
create or replace trigger BIM
before insert on TABLE1
for each row
begin
if not exists (select 1 from TABLE2 where TABLE2.ID = :new.TABLE1.ID)
then
raise_application_error(-20634, 'Error');
end if;
end;
But i'm getting this:
PLS-00049: bad bind variable 'NEW.TABLE1'
Gordon is right, It is preferable to use Foreign Key constraint for this scenario.
The problem with your code ( apart from the error which Gordon pointed out )is that unlike few other DBMS like Postgres, In Oracle you cannot use EXISTS in a PL/SQL expression/statements like IF. It should be a purely SQL statement.
create or replace trigger BIM
before insert on TABLE1
for each row
declare
l_id_exists INT;
begin
select CASE WHEN
exists (select 1 from TABLE2 where TABLE2.ID = :new.ID)
THEN 1
ELSE 0 END INTO l_id_exists from dual;
if l_id_exists = 0
then
raise_application_error(-20634, 'Error');
end if;
end;
/
DEMO
You don't need to repeat the table name:
create or replace trigger BIM
before insert on TABLE1
for each row
begin
if (select 1 from TABLE2 where TABLE2.ID = :new.ID and rownum = 0) is not null
then
raise_application_error(-20634, 'Error');
end if;
end;
That said, this is an odd requirement. I would recommend that you use a foreign key constraint, but you explicitly say "at least once". That leads me to suspect that you have a bad data model -- you are missing some sort of entity where the id would be the primary key of that table.

Values of the inserted row in a Trigger Oracle

I want a trigger that updates the value of a column, but I just want to update a small set of rows that depends of the values of the inserted row.
My trigger is:
CREATE OR REPLACE TRIGGER example
AFTER INSERT ON table1
FOR EACH ROW
BEGIN
UPDATE table1 t
SET column2 = 3
WHERE t.column1 = :new.column1;
END;
/
But as I using FOR EACH ROW I have a problem when I try it, I get the mutating table runtime error.
Other option is not to set the FOR EACH ROW, but if I do this, I dont know the inserted "column1" for comparing (or I dont know how to known it).
What can I do for UPDATING a set of rows that depends of the last inserted row?
I am using Oracle 9.
You should avoid the DML statements on the same table as defined in a trigger. Use before DML to change values of the current table.
create or replace trigger example
before insert on table1
for each row
begin
:new.column2 := 3;
end;
/
You can modify the same table with pragma autonomous_transaction:
create or replace trigger example
after insert on table1 for each row
declare
procedure setValues(key number) is
pragma autonomous_transaction;
begin
update table1 t
set column2 = 3
where t.column1 = key
;
end setValues;
begin
setValues(:new.column1);
end;
/
But I suggest you follow #GordonLinoff answere to your question - it's a bad idea to modify the same table in the trigger body.
See also here
If you need to update multiple rows in table1 when you are updating one row, then you would seem to have a problem with the data model.
This need suggests that you need a separate table with one row per column1. You can then fetch the value in that table using join. The trigger will then be updating another table, so there will be no mutation problem.
`create table A
(
a INTEGER,
b CHAR(10)
);
create table B
(
b CHAR (10),
d INTEGER
);
create trigger trig1
AFTER INSERT ON A
REFERENCING NEW AS newROW
FOR EACH ROW
when(newROW.a<=10)
BEGIN
INSERT into B values(:newROW.b,:newROW.a);
END trig1;
insert into A values(11,'Gananjay');
insert into A values(5,'Hritik');
select * from A;
select * from B;`