Trigger error with column insert - sql

I am creating a trigger for insert.If one of the value is the same as that in the old table. then I print a message. Here is my code.
Create or replace trigger TR_insert_act
After INSERT On ACTIVITIES
For each row
DECLARE
l_act varchar(30);
Begin
select Activity into l_act
From ACTIVITIES;
if(:new.Activity in l_act) then
DBMS_OUTPUT.PUT_LINE ('There is duplicate.');
end if;
end;
It is not compiled with the error on select of l_act, what to do please?

You need a BEFORE trigger, and a WHERE condition.
The DBMS_OUTPUT will not display the error though.
Create or replace trigger TR_insert_act
before INSERT On ACTIVITIES
For each row
l_act number;
Begin
select count(1) into l_act
From ACTIVITIES
WHERE ACTIVITY = :new.Activity
if(l_act > 0 ) then
RAISE_APPLICATION_ERROR (
num => -20000,
msg => 'There is Duplicate');
end if;
end;

Related

How to include a SUBSELECT in VALUES of INSERT to take values from different row?

I want to make a trigger that will insert a value from a connected row. For example I have a table with 3 rows as below:
I create a trigger that will work once row 3 and 4 are deleted (in this case will be deleted at the same time). And I want to record invnr and extinvnr from row 1 based on idparent=id. I cannot seem to make it work though.
CREATE OR REPLACE TRIGGER LOG_DELETEDPAYMENTS
BEFORE DELETE ON payments
FOR EACH ROW
BEGIN
IF :old.invnr IS NULL THEN
INSERT INTO TABLE_LOG_DELETEDPAYMENTS (table_name, invnr, extinvnr, invdate, transactionid, info, createdby, deleted_by, date_of_delete)
values ('payments', :old.invnr, :old.extinvnr, :old.invdate, :old:transactionid, :old.info, :old.createdby, sys_context('userenv','OS_USER'), SYSDATE);
END IF;
END;
How can I incorporate this into the trigger above?
Try it this way:
create or replace TRIGGER LOG_DELETEDPAYMENTS
BEFORE DELETE ON payments
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
Declare
my_invnr PAYMENTS.INVNR%TYPE;
my_extinvnr PAYMENTS.EXTINVNR%TYPE;
Begin
IF :old.INVNR IS NULL THEN
Select INVNR, EXTINVNR
Into my_invnr, my_extinvnr
From PAYMENTS
Where ID = :old.IDPARENT;
--
INSERT INTO TABLE_LOG_DELETEDPAYMENTS (table_name, invnr, extinvnr, invdate, transactionid, info, createdby, deleted_by, date_of_delete)
values ('payments', my_invnr, my_extinvnr, :old.invdate, :old:transactionid, :old.info, :old.createdby, sys_context('userenv','OS_USER'), SYSDATE);
END IF;
End;
END;
You should select the values of INVNR and EXTINVNR based on ID - IDPARENT relationship and store it in the variables (my_invnr and my_extinvnr).
Those variables are used in INSERT into the log statement.
Because of the Select ... Into statement that is reading the affected table - trigger would fail with table PAYMENTS is mutating error.
To avoid that (to separate transaction from the table) you should Declare the PRAGMA AUTONOMOUS_TRANSACTION.
There will be two rows inserted into LOG as the trigger runs FOR EACH (deleted) ROW.
Regards...
This assumes your are on release 12c or greater of Oracle database.
CREATE OR REPLACE PACKAGE LOG_DELETEDPAYMENTS_PKG
AS
-- need a locally defined type for use in trigger
TYPE t_payments_tbl IS TABLE OF payments%ROWTYPE INDEX BY PLS_INTEGER;
END LOG_DELETEDPAYMENTS_PKG;
CREATE OR REPLACE PACKAGE BODY LOG_DELETEDPAYMENTS_PKG
AS
BEGIN
-- could also put the trigger code here and pass the type as a parameter to a procedure
NULL;
END LOG_DELETEDPAYMENTS_PKG;
CREATE OR REPLACE TRIGGER LOG_DELETEDPAYMENTS_CT
FOR DELETE ON payments
COMPOUND TRIGGER
l_tab LOG_DELETEDPAYMENTS_PKG.t_payments_tbl;
l_count PLS_INTEGER:= 0;
BEFORE EACH ROW IS
BEGIN
-- capture the deletes in local type
l_count := l_count + 1;
l_tab(l_count).invnr := :old.invnr;
l_tab(l_count).extinvnr := :old.extinvnr;
l_tab(l_count).invdate := :old.invdate;
l_tab(l_count).transactionid := :old.transactionid;
l_tab(l_count).info := :old.info;
l_tab(l_count).createdby := :old.createdby;
l_tab(l_count).idparent := :old.idparent;
l_tab(l_count).id := :old.id;
END BEFORE EACH ROW;
AFTER STATEMENT IS
BEGIN
FOR i IN l_tab.first .. l_tab.COUNT LOOP
IF(l_tab(i).invnr IS NULL) THEN
-- if the invoice number is NULL, then get info from parent
SELECT p.invnr
,p.extinvnr
INTO l_tab(i).invnr
,l_tab(i).extinvnr
FROM TABLE(l_tab) p
WHERE p.id = l_tab(i).idparent;
END IF;
END LOOP;
-- log all deletes
FORALL i IN 1 .. l_tab.COUNT
INSERT INTO LOG_DELETEDPAYMENTS
(table_name, invnr, extinvnr, invdate, transactionid, info, createdby, deleted_by, date_of_delete)
VALUES
('payments', l_tab(i).invnr, l_tab(i).extinvnr, l_tab(i).invdate, l_tab(i).transactionid, l_tab(i).info, l_tab(i).createdby, sys_context('userenv','OS_USER'), SYSDATE);
l_tab.delete;
END AFTER STATEMENT;
END LOG_DELETEDPAYMENTS_CT;

Trigger to count insert and updates from users

I'm new with oracle sql. I am trying to make a trigger that counts when X user performs an update or an insert, but the TRANSACTIONCONTROL table shows it like this:
DATE--------- USER-----------INSERT----UPDATE
10/03/2022 UserParcial 1 0
10/03/2022 UserParcial 0 1
10/03/2022 UserParcial 1 0
But I want it to look like this:
DATE--------- USER-----------INSERT----UPDATE
10/03/2022 UserParcial 2 1
This is my trigger:
create or replace NONEDITIONABLE TRIGGER TRANSACTIONCONTROL_Trig
AFTER INSERT OR DELETE OR UPDATE on products
for each row
DECLARE
dataTran date;
userTran varchar(30);
InsertTran number:=0;
UpdateTran number:=0;
BEGIN
SELECT SYSDATE INTO dateTran FROM DUAL;
SELECT USER INTO userTran FROM DUAL;
IF INSERTING THEN
InsertTran := InsertTran +1;
INSERT INTO TransactionControl(date, user, insert, updates)
VALUES(dateTran, userTran, insertTran, updateTran);
END IF;
IF UPDATING THEN
updateTran:= updateTran+1;
INSERT INTO TransactionControl(date, user, insert, updates)
VALUES(dateTran, userTran, insertTran, updateTran);
END IF;
END;
If you don't need exact numbers, than mining ALL_TAB_MODIFICATIONS periodically could probably suffice. (I'm curious as to what business function having the count provides)
But if you really must use a trigger, then a compound trigger lets you keep counts at row level, but then summarise at statement level.
Some pseudo code below
create or replace trigger mytrig
for insert or update on mytable
compound trigger
ins_cnt int;
upd_cnt int;
before statement is
begin
ins_cnt := 0;
upd_cnt := 0;
end before statement;
after each row is
begin
if inserting then ins_cnt := ins_cnt + 1; end if;
if updating then upd_cnt := upd_cnt + 1; end if;
end after each row;
after statement is
begin
insert into txn_control ( ... ) values (ins_cnt, upd_cnt);
end after statement;
end;
/

How to insert data into table even if trigger fails?

Oracle 11.1
I have custom logging table where I insert data:
CREATE TABLE log_table
(
message VARCHAR2(255),
created_by VARCHAR2(40) NOT NULL,
created_at DATE NOT NULL,
);
I have a trigger that runs on a specific table which does some checkings. My problem is: when the trigger fails, I want to be able to log some data into the log_table.
Trigger:
CREATE OR REPLACE TRIGGER my_trigger
FOR INSERT OR UPDATE OF column
ON my_table
COMPOUND TRIGGER
BEFORE STATEMENT IS
BEGIN
// code
END BEFORE STATEMENT;
BEFORE EACH ROW IS
BEGIN
IF (/*condition for failing*/) THEN
EXECUTE IMMEDIATE 'INSERT INTO mesaj_ama VALUES (:my_message, :my_user, :my_data)'
USING 'custom error message', SYS.LOGIN_USER, SYSDATE;
RAISE_APPLICATION_ERROR(-20001, 'some error');
END IF;
END BEFORE EACH ROW;
END my_trigger;
/
The following code doesn't work. I tried to use EXECUTE IMMEDIATE maybe to force it, but didn't work. I know that in case of an error, there is automatically a table rollback (which means that the INSERT command is cancelled), but I need a way to do this. Any help?
The concept you're looking for is an Autonomous Trasnaction, eg
CREATE OR REPLACE TRIGGER log_sal
BEFORE UPDATE OF salary ON emp FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO log (
log_id,
up_date,
new_sal,
old_sal
)
VALUES (
:old.employee_id,
SYSDATE,
:new.salary,
:old.salary
);
COMMIT;
END;
Yes, PRAGMA AUTONOMOUS_TRANSACTION seems to be the answer. Here is the working code:
Defined a procedure for logging:
CREATE OR REPLACE PROCEDURE log_error(p_error log_table.message % TYPE) AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO log_table
VALUES (p_error, SYS.LOGIN_USER, SYSDATE);
COMMIT;
END;
and the trigger which calls the procedure:
CREATE OR REPLACE TRIGGER my_trigger
FOR INSERT OR UPDATE OF column
ON my_table
COMPOUND TRIGGER
BEFORE STATEMENT IS
BEGIN
// code
END BEFORE STATEMENT;
BEFORE EACH ROW IS
BEGIN
IF (/*condition for failing*/) THEN
log_error('custom error message');
RAISE_APPLICATION_ERROR(-20001, 'custom error message');
END IF;
END BEFORE EACH ROW;
END my_trigger;
/

sql insert procedure: insert values into table and control if value is existing in another table

I have little problem. I am trying to insert value into table. This is working. But I would like to control if value id_trainer is existing in another table. I want this -> execute insertClub(1, 5, 'someName'); -> and if id_trainer 5 not exists in table Trainer, procedure gives me message about this. (I tried to translate it into eng. lng., so you can find some gramm. mistakes)
create or replace procedure insertClub
(id_club in number, id_trainer in number, clubName in varchar2)
is
begin
declare counter number;
select count(*) into counter from trianer tr where tr.id_trainer = id_trainer;
if counter = 0 then
DBMS_OUTPUT.PUT_LINE('Trainer with this ID not exists');
end if;
insert into club values(id_club, id_trainer, clubName);
exception
when dup_val_on_index then
DBMS_OUTPUT.PUT_LINE('Dup ID');
end;
/
There is some error in the procedure. Please run below code to create procedure:
create or replace procedure insertClub
(id_club in number, id_trainer in number, clubName in varchar2)
is
counter number;
begin
select count(*) into counter from trianer tr where tr.id_trainer = id_trainer;
if counter = 0 then
DBMS_OUTPUT.PUT_LINE('Trainer with this ID not exists');
end if;
insert into club values(id_club, id_trainer, clubName);
exception
when dup_val_on_index then
DBMS_OUTPUT.PUT_LINE('Dup ID');
end;
/

Trigger error "ORA-01403: no data found" when It is not fire

I am creating a trigger below, as long as I insert something already existed in the table, then print other wise do nothing.
create or replace trigger TR_insert
Before INSERT On A
For each row
DECLARE
l_act integer;
l_name varchar(30);
l_minute integer;
Begin
select count(1) into l_act
From A
Where Activity=:new.Activity;
if(l_act>0) then
DBMS_OUTPUT.PUT_LINE ('There is duplicate.');
else
DBMS_OUTPUT.PUT_LINE('');
end if;
end;
It is working fine when I insert something existed, but when I insert new item, then it gives out error "SQL Error: ORA-01403: no data found", why is that please?
You're getting the error because your select does not return any value for your new item. The below shud work.
begin
select count(1) into l_act
From A
Where Activity=:new.Activity;
if(l_act>0) then
DBMS_OUTPUT.PUT_LINE ('There is duplicate.');
end if;
exception
when NO_DATA_FOUND then
DBMS_OUTPUT.PUT_LINE('');
end;