sql trigger using two tables data - sql

So I have three tables. First two have ID column and "rank" column (an integer). I will be inserting into the third table both IDs (ID1, ID2) but I need the trigger to check whether they are the same rank before I can insert. I can't get it to work.
CREATE OR REPLACE TRIGGER TRIGGER1
AFTER INSERT ON TABLE_C
BEGIN
IF NOT EXISTS (
SELECT TABLE_A.id, TABLE_B.id
FROM TABLE_A JOIN TABLE_B ON TABLE_A.rank = TABLE_B.rank
WHERE TABLE_A.id = inserted.id1 AND TABLE_B.id = inserted.id2 )
THEN
PRINT 'Not the same rank'
ROLLBACK
END
END;
I'm using Oracle db.

you can not use 'Rollback' or 'commit' in Oracle Triggers -
please refer this
I think you can modify your trigger as follow -
CREATE OR REPLACE TRIGGER Trigger1
AFTER INSERT ON Table_c
FOR EACH ROW
DECLARE
l_Count NUMBER := 0;
BEGIN
SELECT COUNT(*)
INTO l_Count
FROM Table_a
JOIN Table_b ON Table_a.Rank = Table_b.Rank
WHERE Table_a.Id = :NEW.Id1
AND Table_b.Id = :NEW.Id2;
IF l_Count = 0 THEN
DELETE FROM Table_c
WHERE Id1 = :NEW.Id1
AND Id2 = :NEW.Id2;
--PRINT 'Not the same rank'
Dbms_Output.Put_Line('Not the same rank');
END IF;
END;
/

Triggers are two kinds, BEFORE and AFTER Triggers. If you want to check whether the data has the same rank before the data is inserted, then you should use a BEFORE Trigger.
CREATE OR REPLACE TRIGGER TRIGGER1
BEFORE INSERT ON TABLE_C
Then the logic will follow.
UPDATE: You can easily raise application error. Such as:
IF HasSameRank == 0 THEN
raise_application_error(-21013, 'Not the same rank');
END IF;

Related

How to update a table with a trigger

I am trying to create a trigger that reads from table 2 and updates a column in table 1.
I tried with this method but an exception occurred: ORA-04091:table table1 is mutating .
CREATE OR REPLACE TRIGGER "TRG1"
AFTER INSERT OR UPDATE ON table1
FOR EACH ROW
BEGIN
UPDATE table1 SET name =(SELECT name FROM table2
WHERE table1.id = table2.id);
END;
Use a BEFORE UPDATE trigger (as you cannot modify values after they have been inserted or updated) and use the :NEW record (rather that trying to update the table and getting into an infinite loop of triggers):
CREATE OR REPLACE TRIGGER TRG1
BEFORE INSERT OR UPDATE ON table1
FOR EACH ROW
BEGIN
SELECT name
INTO :NEW.name
FROM table2
WHERE :NEW.id = id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
:NEW.name := NULL;
END;
/
fiddle
You can only update the name in a before trigger
CREATE OR REPLACE TRIGGER "TRG1"
BEFORE INSERT OR UPDATE ON table1
FOR EACH ROW
DECLARE new_name varchar2(100);
BEGIN
SELECT name INTO new_name FROM table2
WHERE :new.id = table2.id;
:new.name :=new_name;
END;
/
MTO was so nice to make a fiddle

Inserting values into a table if the condition in a select query isnt met

I have a table t1 in my oracle database
id name
1 A
2 B
3 C
4 D
5 E
I have used a sequence to add the ids to the table
INSERT INTO t1 (t1seq.nextval, 'A')
I want to now select the id associated with a specific name from the table and if it doesn't exist, insert the name into t1.
SELECT FROM t1 WHERE name = 'F'
should insert a row to t1 if it doesn't exist, I'm trying to use IF EXISTS in oracle but receiving errors.
try this trick
SQL :
IF EXISTS(SELECT * FROM t1 WHERE name = 'F')
BEGIN
RAISERROR (15600,-1,-1, 'DATA EXIST');
RETURN
END
INSERT INTO t1(id,name) values (t1seq.nextval, 'F')
thank u.
You can use the function something like this
...
BEGIN
SELECT ID INTO LV_ID FROM T1 WHERE NAME = P_IN_NAME;
EXCEPTION WHEN NO_DATA_FOUND THEN
LV_ID := NULL;
END;
IF LV_ID IS NULL THEN
LV_ID := T1SEQ.NEXTVAL;
INSERT INTO T1(ID, NAME) VALUES (LV_ID, P_IN_NAME);
END IF;
...
RETURN LV_ID;

ORACLE update with returning OLD and NEW values

Is there equivalent to this T-SQL query in PL/SQL (Oracle 12c)?
UPDATE A SET A.columnA = 10 WHERE A.columnB < 30 OUTPUT INSERTED.*, DELETED.*
The query updates table A and at the same time returns the status of the record before the update and after the update.
Trigger is not a solution for me as well as SELECT records before and SELECT records after updating.
Not a direct one, but using RETURNING INTO you will be able to achieve the same effect:
CREATE TABLE A(columnA VARCHAR2(10), columnB INT);
INSERT INTO A(columnA, columnB) VALUES ('Test', 10);
INSERT INTO A(columnA, columnB) VALUES ('Row 2', 20);
CREATE TABLE audit_table(col_new VARCHAR2(10),col_old VARCHAR2(10));
DECLARE
TYPE rec IS RECORD (actual A.columnA%TYPE, old A.columnA%TYPE);
TYPE col_a_t IS TABLE OF rec;
v_a col_a_t;
BEGIN
UPDATE (SELECT A.*, (SELECT A.columnA FROM dual) AS old_columnA FROM A)
SET columnA = 'XYZ'
WHERE columnB < 30
RETURNING columnA, old_columnA BULK COLLECT INTO v_a;
COMMIT;
-- printing for debug
FOR i IN v_a.first .. v_a.last LOOP
dbms_output.put_line('Old =>' || v_a(i).old || ' new => ' || v_a(i).actual);
END LOOP;
-- additional
FORALL i IN v_a.first .. v_a.last
INSERT INTO audit_table VALUES v_a(i);
COMMIT;
END;
/
SELECT * FROM A;
SELECT * FROM audit_table;
DBFiddle Demo
Idea taken from: Returning Old value during update

How to create function that updates changes in one table from another table?

I have this function that I found here:
Insert trigger to Update another table using PostgreSQL
CREATE TABLE table1
(
id integer NOT NULL,
name character varying,
CONSTRAINT table1_pkey PRIMARY KEY (id)
)
CREATE TABLE table2
(
id integer NOT NULL,
name character varying
)
CREATE OR REPLACE FUNCTION function_copy() RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO
table2(id,name)
VALUES(new.id,new.name);
RETURN new;
END;
$BODY$
language plpgsql;
CREATE TRIGGER trig_copy
AFTER INSERT ON table1
FOR EACH ROW
EXECUTE PROCEDURE function_copy();
If I insert these two rows:
insert into table1 values (1, 'Andrey');
insert into table1 values (2, 'Mariza');
Then they also go into table2.
My problem is when I do an update on a value:
update table1 set name = 'Andi' where id = '1';
nothing happens in table2.
How can I create a function that updates changes in table?
To support UPDATE you can do this:
Trigger:
CREATE TRIGGER trig_copy
AFTER INSERT OR UPDATE ON table1
FOR EACH ROW
EXECUTE PROCEDURE function_copy();
Function:
CREATE OR REPLACE FUNCTION function_copy() RETURNS TRIGGER AS
$BODY$
BEGIN
if TG_OP='INSERT' then
INSERT INTO table2(id,name) VALUES(new.id,new.name);
end if;
if TG_OP='UPDATE' then
Update table2 set name=new.name where id=old.id;
end if;
RETURN new;
END;
$BODY$
language plpgsql;
create or replace trigger trig_copy
after insert or update
on table1
begin
merge into table2 t2
using (
select
id,
name
from
table1 t1) t_new
on (t2.id = t_new.id)
when matched then
update
set
t2.name = t_new.name
where
t2.id = t_new.id
WHEN NOT MATCHED then
insert (id,name) values(t_new.id,t_new.name);
end;
This will be applied to all records, not only updated record.
If U want to update/insert only one record Create trigger
referencing new as new
for each row
and use :new.id :new.name , to find record You want in t2.
Thanks to all for help. This is the answer that had worked for me.
I took it from Elads and Justas solutions:
CREATE TRIGGER trig_copy
AFTER INSERT OR UPDATE ON table1
FOR EACH ROW
EXECUTE PROCEDURE function_copy();
CREATE OR REPLACE FUNCTION function_copy() RETURNS TRIGGER AS
$BODY$
BEGIN
if TG_OP='INSERT' then
INSERT INTO table2
(SELECT * FROM table1
WHERE id NOT IN
(SELECT id FROM table2));
end if;
if TG_OP='UPDATE' then
Update table2 set name=new.name where id=old.id;
end if;
RETURN new;
end;
$BODY$
language plpgsql;

After insert Trigger

I'm trying to do a trigger which insert some into column after insert statement. For example I have table with column which looks:
Column1 Column2 Column3
And I'm inserting data into Column 1, Insert into Table(Column1) values ('256234','234234').
Now I would like automatically insert into COlumn2 TImestamp and into Column3 Value "Y", So output should looks:
Column1 Column2 Column3
256234 2015-10-28 08:48 Y
234234 2015-10-28 08:48 Y
Guys, could you help me with that? I tried to use cursor
Finally I got something like that:
create or replace trigger name
after insert on table
declare
c1 sys_refcursor;
idx varchar2(200);
begin
open c1 for select Column1 from table ;
loop
fetch c1 into idx;
exit when c1%NOTFOUND;
update table a1 set a1.Column2 = (select to_char(sysdate,'YYYYMMDDHHMISS') from dual) where Column1=idx;
update table a1 set a1.Column3 = (select 'Y' from dual) where Column1=idx;
end loop;
close c1;
end;
It works fine, but I'm wondering if there is some other better solution than that?
No need for a cursor or even an update:
create or replace trigger name
before insert on table_x
begin
:new.column2 := sysdate;
:new.column3 := 'Y';
end;
/
But you need a before trigger for this, because an after trigger cannot modify the newly inserted row.
But why don't you just define a default value for those columns, then you don't need a trigger at all:
create table table_x
(
column_1 integer,
column_2 date default sysdate,
column_3 varchar(1) default 'Y'
);
You can create trigger as similar :
CREATE OR REPLACE TRIGGER "TRG_NAME"
BEFORE INSERT ON "TABLE_NAME"
FOR EACH ROW
DECLARE
BEGIN
:NEW.Column2 := to_char(sysdate,'YYYYMMDDHHMISS');
:NEW.Column3 := 'Y';
END TRG_NAME ;
/
ALTER TRIGGER "TRG_NAME" ENABLE;
/
Hope this PL/SQL will help you..