Trigger that checks if the same tuple exists in two different tables - sql

I wrote the following code:
CREATE OR REPLACE TRIGGER CHECK_tuple
BEFORE INSERT ON tableB
FOR EACH ROW
DECLARE IS_JOIN BOOLEAN:=FALSE
BEGIN
SELECT tableB.column1, tableB.column2,
CASE
WHEN IS_JOIN:= FALSE THEN raise_application_error(-20101, 'ERROR.');
ELSE IS_JOIN:= TRUE
END AS CHCK_JOIN
FROM tableB
JOIN tableA
ON tableB.column1=tableA.column1 AND tableB.column2=tableA.column2;
END;
I have to check if a tuple (t1) exits in table A (with "tuple", i mean the entire row of the table with multiple columns). If exists, it has to match with t2 in table B. Before one inserts tuple t2 in table B, the trigger must activate. If t1 doesn't match with t2, the flag IS_JOIN will remain FALSE and Oracle SQL will give an error. Else, if t1 is equal to t2, IS_JOIN will be TRUE and no action will be take. I want this "check" to take place for each row that one will insert in table B. Is this the proper way to do it? If the task isn't clear, please ask for further info.

The proper way to do something like this:
CREATE OR REPLACE TRIGGER test_test_CHECK_tuple
BEFORE INSERT ON tableB
FOR EACH ROW
declare
v_cnt number(10);
BEGIN
SELECT count(*)
into v_cnt
FROM tableA
where column1=:new.column1
and column2=:new.column2;
if v_cnt= 0 then
raise_application_error(-20101, 'ERROR.');
end if;
END;
:new means, that are the values, you want to insert. There is no other way to use that values.
btw. that is not really how a foreign key works, since a foreign key is assigned to a primary key or unique key

Related

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$$

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

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;

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;`

Insert row into SQL table based on existing row

I need to add a new row to a table in Oracle. The problem is that the table has 50 columns and I really don't want to write them all out for an INSERT statement. I tried to do a SELECT INTO statement to duplicate the row and then change the fields I care about individually, but this results in a UNIQUE violation on the primary key. So what I really want to do is declare a variable that holds one row without naming all the columns, change the primary key field, and then insert that variable. How do?
You can use %ROWTYPE in an anonymous PL/SQL block to declare a record representing a row from a table and then select a row into that record and change the primary key and insert the updated record. You can even re-use it for multiple inserts:
DECLARE
rec SOME_TABLE%ROWTYPE;
BEGIN
SELECT *
INTO rec
FROM SOME_TABLE
WHERE A = 1; -- Primary Key
rec.A := 2; -- Change the primary key value.
INSERT INTO SOME_TABLE VALUES rec;
rec.A := 3; -- Change the primary key again.
INSERT INTO SOME_TABLE VALUES rec;
FOR i IN 4 .. 9 LOOP
rec.A := i; -- Change it repeatedly...
INSERT INTO SOME_TABLE VALUES rec;
END LOOP;
FOR i IN 1 .. 3 LOOP
rec.A := SOME_SEQUENCE.NEXTVAL; -- Or you can manage the primary key's value using a sequence.
INSERT INTO SOME_TABLE VALUES rec;
END LOOP;
END;
/
SQLFIDDLE
I have often wanted to do something similar to this, but it's just not possible in any SQL variant I know of. You cannot ask for only some of the columns in a table without explicitly naming them (or perhaps defining a view on them in advance).
The only shortcut I can suggest is to dump the list of column names into a convenient location and then just copy it into an insert statement, changing only the value you need:
insert into foo (select 'newC1' as c1, c2, c3, c4, ..., c50 from foo where bar='baz');
::edit:: In fact, I do this so often that I wrote a Python script to help me. I tell it what table I'm editing, some where clause that matches exactly 1 row, the list of column(s) I want to change, and the list of new value(s) I want in those columns. Then it does the rest.