Trying to create a trigger but get error PLS-00103 - oracle

create or replace trigger newcontract
before insert on contract
declare numcon int;
for each row
begin
select contractcount
into numcon from task
where task.taskid = old.taskid;
if numcon < 3 then
insert into contract values(taskid, workerid, payment);
else
dbms_output.put_line('task is full');
end if;
end;
Gives this cryptic error
Error(1,5): PLS-00103: Encountered the symbol "FOR" when expecting one of the following: begin function pragma procedure subtype type <an identifier> <a double-quoted delimited-identifier> current cursor delete exists prior
The record being inserted into contract should not be inserted if contract count for that task is about 2. So I need to check the value of contractcount for each record being inserted. I use a select statement to get the value, but I get this error.

You have more than one problem here:
declare section(the part of the trigger where you declare your variables) goes after the for each row part
OLD and NEW values are "accessed" like this : :new.column_name and :old.column_name
The :old value in before insert trigger is always null because you are inserting a new value, there is no old value, only new value.
If you want to prevent insert if some value is smaller than 3 then you can do it like this:
create or replace trigger newcontract
before insert on contract
for each row
declare
numcon int;
begin
select contractcount
into numcon
from task
where task.taskid = :new.taskid;
if numcon < 3 then
raise_application_error(-20000, 'Task is full');
end if;
end;
/
Here is a small demo
For more info please do add some more detailed description and some sample data where you show us what kind of data you want to be able to insert and why and what kind of data you do not want to insert and why.

Related

How can I get the inserted primary key value from AFTER INSERT trigger in Oracle?

My Oracle DB has a table DOC_WF_COMM and its primary key is DWFC_ID. Primary key value is based on a sequence called SQ_DOC_WF_COMM.
I have created a row level AFTER INSERT trigger on that table and inside the trigger I need to join the inserted record with some other tables like this:
create or replace TRIGGER TRG_DOC_WF_COMM_AFT_INS AFTER INSERT ON DOC_WF_COMM REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
L_SUBJECT VARCHAR2(300);
L_BODY CLOB;
L_PNT_CODE VARCHAR(100) := NULL;
L_DR_PRJ_ID NUMBER(12);
L_STR_EMAIL VARCHAR2(120);
L_DWFC_TO_USR_ID VARCHAR2(12);
L_PNT_ID NUMBER(12);
L_PNT_EMAIL_YN VARCHAR(1);
L_PNT_ACTIVE_YN VARCHAR(1);
L_PNT_NOTIFY_YN VARCHAR(1);
BEGIN
IF INSERTING THEN
L_PNT_CODE := 'WFNT_MESSAGE';
SELECT DR_PRJ_ID, STR_EMAIL, DWFC_TO_USR_ID INTO L_DR_PRJ_ID, L_STR_EMAIL, L_DWFC_TO_USR_ID
FROM DOC_WF_COMM
JOIN DOC_WF_USERS ON DWFU_ID = DWFC_DWFU_ID
JOIN DOC_WORKFLOW ON DWF_ID = DWFU_DWF_ID
JOIN DOCUMENT_REF ON DR_ID = DWF_DR_ID
JOIN ST_REGISTER ON STR_ID = DWFU_STR_ID
WHERE DWFC_ID = :NEW.DWFC_ID AND DWFC_RESPONSE IS NULL;
-- SOME QUERIES HERE
END IF;
END;
The trigger is compiled successfully and when I insert record into DOC_WF_COMM table I get this error:
ORA-01403: no data found ORA-06512
The error is :NEW.DWFC_ID in WHERE clause and I have change it to these values:
:OLD.DWFC_ID
SQ_DOC_WF_COMM.NEXTVAL
SQ_DOC_WF_COMM.CURRVAL
But no any luck. Any idea why this error is and how can I resolve it?
The problem is this line in your trigger:
PRAGMA AUTONOMOUS_TRANSACTION;
That means the trigger executes as an isolated transaction in a separate session, which means it cannot see the uncommitted state of any other session. Crucially this includes the session which fires the trigger, so the autonomous transaction cannot see the record you just inserted. Hence, NO_DATA_FOUND.
You haven't posted the whole trigger or explained what you're trying to do, so only you know why you have included the PRAGMA. However, the chances are you don't need it. Remove the PRAGMA (and the COMMIT) and your trigger should work just fine.
If I understood you correctly, create a local variable and put the next sequence value in there. Then it can be referenced throughout the code, always having the same value. Something like this:
declare
l_seq number := my_seq.nextval;
begin
insert into table_a (id, ...) values (l_seq, ...);
update table_b set id = l_seq where ...
select ... into ... from ... where id = l_seq;
end;
I changed the query inside the trigger to this, it is working fine
SELECT STR_PRJ_ID, STR_EMAIL, :NEW.DWFC_TO_USR_ID INTO L_DR_PRJ_ID, L_STR_EMAIL, L_DWFC_TO_USR_ID
FROM DOC_WF_USERS, ST_REGISTER
WHERE :NEW.DWFC_TO_USR_ID = DWFU_US_ID AND DWFU_STR_ID = STR_ID AND DWFU_ID = :NEW.DWFC_DWFU_ID;
Not sure why is that. If anyone can figure out the mistake in the query given in the question, please let me know. Thanks

Oracle PL/SQL bad bind variable

I checked all my fields and everything is fine but i keep on getting this error
When i write this trigger on sql command it says trigger has been created but when i checked on the trigger it showed invalid
This is my trigger
create or replace trigger "Max_Crew_T1"
Before insert or update on "CREW"
for each row
declare
v_count number;
Begin
select count(*) into v_count from CREW where AirPlaneID=:NEW.AirPlaneID;
if
v_count > 5
then
Raise_Application_ERROR(-20343,'Crew number exceeded');
END IF;
END;
HERE IS THE ERROR MESSAGE;
PLS-00049: bad bind variable 'NEW.AIRPLANEID'
click on the link below for table desc
The error is because you defined the airplane id while creating the table in quotes. I.e "AirPlaneID" hence you need to do the same in the trigger.
It is not a good practice to define case-sensitive columns with quotes as it causes all these issues, its much better to define without quotes eg. create table crew (airplaneid number(6))..etc. Anyway..
Please use below:
create or replace trigger "Max_Crew_T1"
Before insert or update on "CREW"
for each row
declare
v_count number;
Begin
select count(*) into v_count from CREW where "AirPlaneID"=:NEW."AirPlaneID";
if
v_count > 5
then
Raise_Application_ERROR(-20343,'Crew number exceeded');
END IF;
END;

How to declare one variable for each row in trigger?

I have following trigger:
create or replace TRIGGER MY_TIGGER_NAME AFTER UPDATE ON MY_TABLE
REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW
WHEN ( NEW.STATUS = ANY (10,40,42,44,46,50,60) and OLD.STATUS != NEW.STATUS)
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
IF :NEW.ALERT is NULL
THEN
dbms_alert.signal('print_update_event','update_message');
ELSE
dbms_alert.signal( :NEW.ALERT,'update_message');
END IF;
commit;
END;
I would like to change it because it send alert for each row. I would like to send only one alert if more than one row with ALERT column equal NULL was updated and I would like to send one alert for each row with ALERT column NOT equal NULL.
As I understand Oracle variables I can declare local variable in my trigger but this variable will be declared separately for each row so following change has no sense:
create or replace TRIGGER MY_TIGGER_NAME AFTER UPDATE ON MY_TABLE
REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW
WHEN ( NEW.STATUS = ANY (10,40,42,44,46,50,60) and OLD.STATUS != NEW.STATUS)
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
flag NUMBER(1,0) :=0;
BEGIN
IF :NEW.ALERT is NULL and flag=0
THEN
dbms_alert.signal('print_update_event','update_message');
flag:=1;
ELSE
dbms_alert.signal( :NEW.ALERT,'update_message');
END IF;
commit;
END;
Package variable could be used instead local variable but I think that package variable is bad idea because two triggers can be executed in parallel. Maybe I am wrong.
I attach diagram of trigger which shows what I would like to achieve.
How to do it?

Accessing the trigger table inside the trigger's body

How can I access a table that has a trigger on inside the trigger's body?
create or replace trigger insert_try after insert on triggerTable for each row
begin
insert into anotherTable (triggerFunction(:new.field1), 155,155);
end;
create or replace function triggerFunction(param1 in number) return number as
abc number;
begin
select max(field1) into abc from triggerTable where field1!= param1;
return abc ;
end triggerFunction;
This results in "SQLSyntaxErrorException: ORA-04091". Is there any way to do the insert operation independent of the trigger and run the trigger afterwards?
I don't see any reason to use the function which queries the same table on which the trigger is defined. It does nothing but sends the :new.field1 as parameter and get's the same value back.
Since you already use FOR EACH ROW, you can access the old and new values as :old and :new.
CREATE OR REPLACE TRIGGER insert_try
AFTER INSERT ON triggerTable
FOR EACH ROW
BEGIN
INSERT INTO anotherTable
(:new.field1, 155,155
);
END;
/
It will simply insert the value from field1 of triggertable into anothertable.
Make the function autonomous by using pragma autonomous_transaction
create or replace function triggerFunction(param1 in number) return number as
abc NUMBER;
pragma autonomous_transaction ;
begin
select max(field1) into abc from triggerTable where field1!= param1;
return abc ;
end triggerFunction;
Then try inserting , mutating table error will not come in this case .

Simple oracle insert

I am trying to simply insert some information to a table in Oracle using Forms. Sometimes the insert statement works, sometimes it doesn't. I'm just not experienced enough with Oracle to understand what's not working. Here's the code:
PROCEDURE create_account IS
temp_name varchar2(30);
temp_street varchar2(30);
temp_zip number(5);
temp_phone varchar2(30);
temp_login passuse.login%type;
temp_pass varchar2(30);
temp_total number(4);
temp_lgn passuse.lgn%type;
cursor num_cursor is
select MAX(ano)
from accounts;
cursor lgn_cursor is
select MAX(lgn)
from passuse;
BEGIN
temp_name:= Get_Item_Property('ACCOUNTS.A_NAME', database_value);
temp_street:= Get_Item_Property('ACCOUNTS.STREET', database_value);
temp_zip:= Get_Item_Property('ACCOUNTS.ZIP', database_value);
temp_phone:= Get_Item_Property('ACCOUNTS.STREET', database_value);
temp_login:= Get_Item_Property('PASSUSE.LOGIN', database_value);
temp_pass:= Get_Item_Property('PASSUSE.PASS', database_value);
open num_cursor;
fetch num_cursor into temp_total;
open lgn_cursor;
fetch lgn_cursor into temp_lgn;
if(lgn_cursor%found) then
if(num_cursor%found) then
temp_lgn := temp_lgn + 20;
--the trouble maker..
INSERT INTO passuse (lgn, a_type, login, pass)
VALUES (temp_lgn, 1, temp_login, temp_pass);
temp_total := temp_total+1;
INSERT INTO accounts(ano,lgn,a_name,street,zip,phone)
VALUES (temp_total,temp_lgn,temp_name,temp_street,temp_zip,temp_phone);
end if;
end if;
close lgn_cursor;
close num_cursor;
commit;
END;
To expand on #Mikpa's comment - it certainly appears that getting the values for ACCOUNTS.ANO and PASSUSE.LGN from sequences would be a good idea. Populating these fields automatically by using a trigger would also be helpful. Something like the following:
-- Note that the following is intended as a demo. When executing these statements
-- you'll probably have to modify them for your particular circumstances.
SELECT MAX(ANO) INTO nMax_ano FROM ACCOUNTS;
SELECT MAX(LGN) INTO nMax_lgn FROM PASSUSE;
CREATE SEQUENCE ACCOUNTS_SEQ START WITH nMax_ano+1;
CREATE SEQUENCE PASSUSE_SEQ START WITH nMax_lgn+1;
CREATE TRIGGER ACCOUNTS_BI
BEFORE INSERT ON ACCOUNTS
FOR EACH ROW
BEGIN
SELECT ACCOUNTS_SEQ.NEXTVAL
INTO :NEW.ANO
FROM DUAL;
END ACCOUNTS_BI;
CREATE TRIGGER PASSUSE_BI
BEFORE INSERT ON PASSUSE
FOR EACH ROW
BEGIN
SELECT PASSUSE_SEQ.NEXTVAL
INTO :NEW.LGN
FROM DUAL;
END PASSUSE_BI;
Having done the above you can now write your inserts into these tables as
INSERT INTO passuse (a_type, login, pass)
VALUES (1, temp_login, temp_pass)
RETURNING LGN INTO temp_lgn;
and
INSERT INTO accounts(lgn, a_name, street, zip, phone)
VALUES (temp_lgn, temp_name, temp_street, temp_zip, temp_phone);
Note that in the both statements the key values (PASSUSE.LGN and ACCOUNTS.ANO) are not mentioned in the field list as the new triggers should take care of filling them in correctly. Also note that when inserting into PASSUSE the RETURNING clause is used to get back the new value for LGN so it can be used in the insert into the ACCOUNTS table.
Share and enjoy.

Resources