trigger for updating a value - plsql

I am a newbie in PLSQL and I would like to create a trigger that checks first if there is a record in a table before making an update.
The code I got so far is:
CREATE OR REPLACE TRIGGER table_bu
BEFORE UPDATE ON employee
FOR EACH ROW
DECLARE
v_employee_id:=employee.employee_ID%TYPE;
BEGIN
SELECT employee_id INTO v_employee_id FROM employee;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR (-20001,'data not found');
END;
How I can create a trigger that checks up if a record exists in the table and if it does not exists does not allow the update.
My table estructure is:
employee_id NUMBER
employee_name VARCHAR(20)
employee_salary NUMBER
...
Thanks

You are on a wrong way. The trigger as it is will throw runtime 'Mutating table' error even after fixing syntax error - you missed semicolon after raise_application_error(also it should take 2 arguments, not one). Correct syntax :
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR (-20001, 'data not found'); -- 1st parameter -error code
Update
As far as I understand the updated version of the question, you want to show error if record doesn't exist. The problem with row level trigger approach is that it won't be executed if nothing is found due to condition in WHERE. The simplest way is to check number of rows affected on client side and raise an error there. Or you can write a procedure that checks sql%rowcount after executing desired update, and then throw an exception if it's 0.
If you prefer to do in a hard way, you can create package variable which of type employee.employee_ID%TYPE, before update statement level trigger that resets variable (say set it to null), after update row level trigger that sets this variable to NEW.employee_ID, and after update statement level trigger that throws an exception if the variable is null. Note: this will properly work for individual updates only.

"How I can create a trigger that checks up if a record exists in the table and if it does not exists does not allow the update."
There is really only one practical way to do this - use a referential constraint (foreign key).

Related

How To Create a PL/SQL Trigger That Detects an Inserted or Updated Row and updates a Record in a Different Table?

I am creating a book tracking database for myself that holds information about my books and allows me to keep track of who is borrowing them. I am trying to create a trigger on my Checkouts table that runs if a record is added or updated that will determine if a checkout data has been entered or if a checkin date has been entered and change the "available" field in my Books table to "Y" or "N".
I have created a trigger called "update_book_availablility" on my Checkouts table but I keep getting this error:
"PLS-00103: Encountered the symbol 'end-of-file' when expecting one of the following: ( begin case declare and exception exit for goto if loop mod null pragma raise return select update while with <<continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge standard pipe purge json_object
Errors: check compiler log"
Here is my trigger code:
CREATE OR REPLACE NONEDITIONABLE TRIGGER "UPDATE_BOOK_AVAILABILITY"
AFTER INSERT OR UPDATE OF ISBN, PersonID, checkout_date, checkin_date
ON Checkouts
FOR EACH ROW
BEGIN
IF :NEW.checkout_date = NULL
THEN
UPDATE Book
SET available = 'N'
WHERE ISBN IN (SELECT :NEW.ISBN FROM Checkouts);
END IF;
END;
Here is an image of my ERD:
ERD
I have been looking into and double checking my trigger syntax, If condition syntax, subquery syntax, and googling this error but have found nothing that has helped. I am new to PL/SQL and would appreciate any help in understanding what I have done wrong or missed.
PLS-00103: Encountered the symbol end-of-file error is SYNTAX ERROR
Copied your trigger and adjusted it to one of my test tables - it works. I removed NONEDITIONABLE and changed trigger table name as well as column names and table/column beeing updated by trigger.
To Do:
Check your syntax again or write the trigger from scratch once more
"...WHERE ISBN IN (SELECT :NEW.ISBN FROM Checkouts)..." selects one fixed value (FOR EACH ROW) :NEW.ISBN of triggering table, better ->> "... WHERE ISBN = :NEW.ISBN ..."
Prety sure that you don't need NONEDITIONABLE trigger for your books tracking app...
Regards...

Table mutating when "after insert" trigger fired, i don't know what to do

I want to increment the samenameamount column in my table employees after inserting the similar name
I have a such trigger:
create or replace trigger countNumbEmp after insert on employees for each row
declare
var_count_names number;--var for count same names
begin
select count(emp_name) into var_count_names from employees where emp_name = :new.emp_name;
update employees set samenameamount = var_count_names where emp_name = :new.emp_name;
end;
My table is
- Name Null? Type
-------------- ----- ------------
EMP_ID NUMBER
EMP_NAME VARCHAR2(20)
SAMENAMEAMOUNT NUMBER
After inserting i get this message:
- Error report -
ORA-04091: table DASTAN.EMPLOYEES is mutating, trigger/function may not see it
ORA-06512: at "DASTAN.COUNTNUMBEMP", line 4
ORA-04088: error during execution of trigger 'DASTAN.COUNTNUMBEMP'
Here's a brief list of things that you should not do in a trigger (source link here, my personal favorite page):
On insert triggers have no :OLD values.
On delete triggers have no :NEW values.
Triggers do not commit transactions. If a transaction is rolled back,
the data changed by the trigger is also rolled back.
Commits, rollbacks and save points are not allowed in the trigger
body. A commit/rollback affects the entire transaction, it is all or
none.
Unhandled exceptions in the trigger will cause a rollback of the
entire transaction, not just the trigger.
If more than one trigger is defined on an event, the order in which
they fire is not defined. If the triggers must fire in order, you
must create one trigger that executes all the actions in the required
order.
A trigger can cause other events to execute triggers. A trigger can
not change a table that it has read from. This is the mutating table
error issue.
I am intentionally not pasting the last part of that link, because it mentions things like "how to commit in a trigger" etc., as it's a bad practice, even though it IS in fact a workaround. If you can't avoid having that, however, there's something wrong with the design of the solution or the implementation and this needs to be solved first.
Cheers

sqlite trigger causes "no such column" exception

I'm a newbie with sql triggers and am getting an ESQLiteException on what seems like a simple example. When I try to modify the "memberTag" column in an existing row, I get the exception "no such column: memberTag". If I drop the trigger, the exception goes away and the row gets updated.
I'm using SQLite and I'm using the "SQLite Expert Personal" app to do this experimenting.
I have this table:
CREATE TABLE [znode] (
[description] CHAR NOT NULL,
[memberTag] CHAR);
and this trigger:
CREATE TRIGGER [memberTagTrigger]
AFTER UPDATE
ON [znode]
FOR EACH ROW
WHEN length(memberTag)=0
BEGIN
update znode
set memberTag = null;
END;
My update experiment data is something like this:
description memberTag
one x
two (null)
And when I try to change (null) to "y" using SQLite Expert Personal, it throws the exception.
The problem is in the WHEN clause: the database does not know where memberTag comes from, because there are two possible rows, the old one, and the new one.
Use either OLD.memberTag or NEW.memberTag.
(There is another problem: the UPDATE will change all rows in the table, because you forgot the WHERE clause.)

'Numeric or value error' during execution of trigger

I am currently working on an application using Oracle Apex which is being used as a database to keep a record of cabling.
When components in the database are deleted, a PL/SQL trigger is executed which deletes any associations to the components from another table (PORT) and sets the port references to 0.
The trigger seems to work perfectly for the majority of components I attempt to delete, but presents me with a 'numeric or value error' for others and I am struggling to find out why (as shown below)
The trigger being executed looks like this:
create or replace trigger "COMPONENT_TRIGPORT"
AFTER delete
on "COMPONENT"
for each row
declare
TYPE portType IS TABLE OF PORT%ROWTYPE;
port_arr portType;
begin
DELETE FROM PORT
where PORT.COMPONENT_ID = :old.COMPONENT_ID
RETURNING PORT_ID, PORT_NUMBER, COMPONENT_ID, CONNECTOR_ID, PORT_TYPE
BULK COLLECT INTO port_arr;
begin
FOR i IN port_arr.FIRST .. port_arr.LAST
LOOP
update PORT
set PORT.CONNECTOR_ID = 0
where PORT.CONNECTOR_ID = port_arr(i).CONNECTOR_ID;
END LOOP;
end;
end;​
I know that the line giving me the error is
where PORT.COMPONENT_ID = :old.COMPONENT_ID
But I cannot work out why it works fine in some cases but not in others!
Replace
FOR i IN port_arr.FIRST .. port_arr.LAST
with:
FOR i IN 1 .. port_arr.COUNT
If no records are deleted the array will not be initialized and port_arr.FIRST and port_arr.LAST will return null, which will raise the exception ORA-06502: PL/SQL: numeric or value error. COUNT is always safe to use, even on a collection that has not been initialized.

How to fix the mutating trigger in oracle

I wrote the trigger for updating the column value in the same table. For Ex I wrote a trigger on metermaster table after update of assettype column , with in the trigger i am trying to update the instantaneousinterval column in the same metermaster table. Its throws the error like this
ERROR: ORA-04091: table PSEB.METERMASTER is mutating, trigger/function
may not see it.
my trigger code is as follows:
CREATE OR REPLACE TRIGGER PSEB.spiupdate
AFTER
update of assettype
ON pseb.metermaster
referencing new as new old as old
for each row
DECLARE
vassettype number;
resval number(10);
vassettypename varchar2(50);
vmeterid number;
begin
select :new.assettype,:new.meterid INTO vassettype,vmeterid from dual;
select assettypename into vassettypename from pseb.METERASSETINSTTYPE where ASSETTYPEID=vassettype;
select case when assettypename like 'DT' then 86400 when assettypename like 'HT' then 3600 when assettypename like 'FSB' then 86400 end into resval from pseb.meterassetinsttype where assettypename =vassettypename;
update pseb.metermaster set instantaneousinterval=resval where meterid=vmeterid;
end;
I tried to use the
pragma autonomous_transaction;
but it gives the deadlock condition.
ERROR: ORA-00060: deadlock detected while waiting for resource
ORA-06512:
pls help me to fix this issue.
instead of this update statement
update pseb.metermaster set instantaneousinterval=resval where meterid=vmeterid;
use
:new.instantaneousinterval=resval;
A mutating table occurs when a statement causes a trigger to fire and that trigger references the table that caused the trigger. The best way to avoid such problems is to not use triggers, but I suspect the DBA didn’t take the time to do that. He could have done one of the following:
Changed the trigger to an after trigger.
Changed it from a row level trigger to a statement level trigger.
Convert to a Compound Trigger.
Modified the structure of the triggers to use a combination of row and statement level triggers.
Made the trigger autonomous with a commit in it.
Try this pragma autonomous_transaction; with Commit
Since the trigger is updating the same table on which it is defined, why don't you update the two columns in the first update statement itself?
i.e, Instead of using an update like
UPDATE pseb.metermaster
SET assettype = '<v_assettype>';
and relying on trigger to update the instantaneousinterval column, why don't you use an update statement like the following (code is not tested)
UPDATE pseb.metermaster
SET assettype = '<v_assettype>',
instantaneousinterval = (SELECT CASE
WHEN assettypename LIKE 'DT' THEN 86400
WHEN assettypename LIKE 'HT' THEN 3600
WHEN assettypename LIKE 'FSB' THEN 86400
END
FROM pseb.meterassetinsttype
WHERE assettypeid = '<v_assettype>');
In my opinion, using a trigger and autonomous_transaction in this case would be a wrong approach. To know why this is wrong, please search http://asktom.oracle.com/ for this error.

Resources