Issue with trigger for archiving - sql

Trying to make a trigger that puts data into an archive table when a column called COMPLETION_STATUS goes from incomplete to complete, the dbms is a placeholder for the insert but I'm getting the following errors in the if statement
Error(6,1): PLS-00103: Encountered the symbol enter code here"SELECT" when expecting one of the following: begin function pragma procedure subtype type current cursor delete exists prior The symbol "begin" was substituted for "SELECT" to continue.
Error(9,1): PLS-00103: Encountered the symbol "IF" when expecting one of the following: * & - + ; / at for mod remainder rem and or group having intersect minus order start union where connect || multiset The symbol ";" was substituted for "IF" to continue.
Error(13,4): PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: ( begin case declare end 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 pipe purge
Code:
create or replace TRIGGER ARCHIVING_TRIG
BEFORE UPDATE OF COMPLETION_STATUS ON PROJECT_DATA
BEGIN
DECLARE COMPLETION_STATUS1 VARCHAR2(9);
SELECT COMPLETION_STATUS into COMPLETION_STATUS1
FROM PROJECT_DATA WHERE COMPLETION_STATUS = 'complete'
IF COMPLETION_STATUS1 = 'complete'
THEN
DBMS.output('123');
END IF;
END;

The DECLARE block should be before the BEGIN block.
The SELECT ... statement needs to be terminated with a semicolon (;).
It's dbms_output.put_line() not dbms.output();
You're trying to assign the result of a query that potentially can return more than one row to a scalar variable.
The rows selected from project_data have no relation to the one(s) that triggered the trigger.
I suggest you use something like:
CREATE TRIGGER archiving_trig
AFTER UPDATE
ON project_data
FOR EACH ROW
WHEN (old.completion_status <> 'complete'
AND new.completion_status = 'complete')
BEGIN
dbms_output.put_line('Trigger fired for ID ' || :new.id);
END;
db<>fiddle
I think maybe AFTER is the better time, because you want to archive the row after the status was successfully changed.
Because of the WHEN the trigger will only fire if completion_status has been changed from something other than 'complete' to 'complete'. But you maybe also need to have a method of removing entries from the archive when the status changes from 'complete' to something else. That isn't covered here.
Declaring it as FOR EACH ROW let's you access the values of the updated row via :new. That way you don't need a query to select that nor a variable to select into.

I guess you need this:
create table PROJECT_DATA_NEW as select * from PROJECT_DATA where 1=2;
CREATE OR REPLACE TRIGGER ARCHIVING_TRIG
AFTER UPDATE
ON PROJECT_DATA
FOR EACH ROW
DECLARE
status number;
BEGIN
status:=0;
select 1 into status from PROJECT_DATA where
:new.COMPLETION_STATUS='complete' and
:old.COMPLETION_STATUS='incomplete'
if (status=1) then
insert into PROJECT_DATA_NEW values(:old.column1,
:old.column2,
:old.column3,
:old.column4,
:old.column5,....etc);
end if;
END;
/

Related

"If" in a trigger comparing two columns from 2 different tables- error

Im trying to create a trigger when updating table 'test' to make sure a value in a column is not greater than another one from a different table. But I get this error on Oracle Apex: ORA-24344: success with compilation error
'test' is a table and 'chestionar' is a second one, so I want to launch that error when I insert a value in 'punctaj' which is greater than the 'punctaj_max'. And the id of the both tables must be the same . What should I modify?
here is my code:
CREATE OR REPLACE trigger trg_a
BEFORE UPDATE on test
begin
if test.punctaj > chestionar.punctaj_max and test.id=chestionar.id then
raise_application_error(234,'error, the value is grater than maximum of that id');
end if;
end;
I think the logic you want is:
create or replace trigger trg_a
before update on test
for each row
declare
p_punctaj_max chestionar.punctaj_max%type;
begin
select punctaj_max into p_punctaj_max from chestionar c where c.id = :new.id;
if :new.punctaj > p_punctaj_max then
raise_application_error(234, 'error, the value is grater than maximum of that id');
end if;
end;
/
The idea is to recover the value of punctaj_max in table chestionar for the id of the row that is being updated in test (note that this implicitely assumes that there cannot be multiple matching rows in chestionar). We can then compare that to the value being updated, and raise the error if needed.
You have three (I think) issue in your code:
You should apply trigger at time of insert also.
you need to query the table chestionar and then compare the value.
the error number in raise_application_error should be negative and between -20999 and -20000.
So, I would rewrite your code as follows:
create or replace trigger trg_a
before update or insert on test
for each row
declare
lv_cnt number := 0;
begin
select count(1) into lv_cnt
from chestionar c
where c.id = :new.id
and :new.punctaj > c.punctaj_max;
if lv_cnt > 0 then
raise_application_error(-20234, 'error, the value is grater than maximum of that id');
end if;
end;
/

How to change from oracle trigger to oracle compound trigger?

I have to change from given Oracle trigger:
CREATE OR REPLACE TRIGGER MY_TRIGGER
AFTER UPDATE OF STATUS ON T_TABLE_A
FOR EACH ROW
BEGIN
UPDATE T_TABLE_B T
SET T.STATUS = :NEW.STATUS
WHERE T.REF_ID = :NEW.ID;
END;
/
to an Oracle compound trigger. Effect must be the same. My approach is now:
CREATE OR REPLACE TRIGGER MY_NEW_TRIGGER
for insert or update on T_TABLE_A
compound trigger
before statement -- STUB
is
begin
null;
end before statement;
before each row
is
begin
end before each row;
after each row -- STUB
is
begin
--IDEA: collect ids of changed records (T_TABLE_A) here >> in a global variable? array?
end after each row;
after statement -- STUB
is
begin
--IDEA: Bulk Update of T_TABLE_B (goal is: update T_TABLE_B.STATUS column; must be the same as T_TABLE_A.STATUS)
end after statement;
end;
/
But as a Java Developer I am very slow to find out the correct syntax of variables, arrays and simple DB scriptings, so any approach is helpful.
Approach where to start is marked as "IDEA".
As I can see you have almost done the half of the job , I amended the code to IDEA part.
I have not included the before statement part and also the triggering clause and all you can adjust according to your need. I just considered for updating the status column in my code.
CREATE OR REPLACE TRIGGER my_trigger
FOR UPDATE OF status ON t_table_a
COMPOUND TRIGGER
-- record type to hold each record updated in t_table_a
-- columns we are intersted in are id and status
TYPE table_a_rec IS RECORD(
id t_table_a.id%TYPE
,status t_table_a.status%TYPE);
--table type based on record to access each record using this
TYPE table_a_row_data_t IS TABLE OF table_a_rec INDEX BY PLS_INTEGER;
-- global variable for the compound trigger
g_row_level_data table_a_row_data_t;
AFTER EACH ROW IS
BEGIN
-- IDEA: collect ids of changed records (T_TABLE_A) here >> in a global variable? array?
g_row_level_data(g_row_level_data.count + 1).id := :new.id;
g_row_level_data(g_row_level_data.count).status := :new.status;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
--IDEA: Bulk Update of T_TABLE_B (goal is: update T_TABLE_B.STATUS column; must be the same as T_TABLE_A.STATUS)
FORALL i IN 1 .. g_row_level_data.count
UPDATE t_table_b t
SET t.status = g_row_level_data(i).status
WHERE t.ref_id = g_row_level_data(i).id;
END AFTER STATEMENT;
END my_trigger;
/

Error(6,87): PLS-00103: Encountered the symbol "JOIN" when expecting one of the following/compound trigger

When I try to compile below compound trigger I got the error message. Please suggest what can be done to clear those error.
I tried to use normal trigger but its throwing ORA-04091 ERROR.
create or replace TRIGGER "WS5108"."AL_PROJECT_ORACLE_CODE_TRG" FOR
INSERT
ON ITIB_REQUESTS
COMPOUND TRIGGER
DECLARE
V_CODE varchar (200);
BEGIN
IF :NEW."J_PROJECT_ORACLE_CODE" IS NULL THEN
SELECT distinct (PROJECT_ORACLE_CODE) INTO V_CODE
FROM ITIB_PROJECT_ORACLE_CODE POC
JOIN ITIB_VPDOMAIN VP ON (POC.VP_DOMAIN = VP.VP_DOMAIN)
WHERE POC.VP_DOMAIN = (SELECT VP_DOMAIN
FROM ITIB_VPDOMAIN
WHERE ID = :NEW."VP_DOMAIN")
AND CAPEX_CATEGORY = :NEW."C_CAPEX_CATEGORY"
AND :NEW."C_TOTAL_EURO" <= 250 ;
END IF;
:NEW.J_PROJECT_ORACLE_CODE := V_CODE;
EXCEPTION
when no_data_found then
V_CODE := null ;
END AL_PROJECT_ORACLE_CODE_TRG;
I am trying to add value for one column in table after new line inserted in that table if that column is empty. This column value I am taking from that select condition and new values which are inserted in table.
The error in your question title is the second of two; running the reformatted code in your question I see:
LINE/COL ERROR
--------- -------------------------------------------------------------
2/1 PLS-00103: Encountered the symbol "DECLARE" when expecting one of the following: function pragma procedure subtype type <an identifier> <a double-quoted delimited-identifier> current cursor delete exists prior before after instead
8/9 PLS-00103: Encountered the symbol "JOIN" when expecting one of the following: , ; for group having intersect minus order start union where connect
(with different line number due to reformatting). In general you should fix the first error reported first, as subsequent errors are often unhelpful and caused by side-effects of the earlier ones. That is the case here. If you fix the compound trigger syntax to address the error reported against DECLARE then the JOIN is no longer a problem either:
create or replace TRIGGER "WS5108"."AL_PROJECT_ORACLE_CODE_TRG"
FOR INSERT
ON ITIB_REQUESTS
COMPOUND TRIGGER
BEFORE EACH ROW IS
V_CODE varchar (200);
BEGIN
IF :NEW."J_PROJECT_ORACLE_CODE" IS NULL THEN
SELECT distinct PROJECT_ORACLE_CODE INTO V_CODE
...
END IF;
:NEW.J_PROJECT_ORACLE_CODE := V_CODE;
EXCEPTION
when no_data_found then
V_CODE := null ;
END BEFORE EACH ROW;
END AL_PROJECT_ORACLE_CODE_TRG;
/
Basically, the main trigger body code you had needed to be in a BEFORE EACH ROW block, since it is a compound trigger.
Incidentally, you probably want V_CODE to be declared as varchar2 rather than varchar, or as ITIB_PROJECT_ORACLE_CODE.PROJECT_ORACLE_CODE%TYPE. If you need that variable at all; the assignment back to the :NEW field probably wants to be inside the IF block (as if that is not initially null, you set it to v_code, which is then null!), but you can also select straight into the :NEW variable. So I think you really want something more like:
create or replace TRIGGER WS5108.AL_PROJECT_ORACLE_CODE_TRG
FOR INSERT
ON ITIB_REQUESTS
COMPOUND TRIGGER
BEFORE EACH ROW IS
BEGIN
IF :NEW.J_PROJECT_ORACLE_CODE IS NULL THEN
SELECT PROJECT_ORACLE_CODE
INTO :NEW.J_PROJECT_ORACLE_CODE
FROM ITIB_PROJECT_ORACLE_CODE POC
JOIN ITIB_VPDOMAIN VP ON (POC.VP_DOMAIN = VP.VP_DOMAIN)
WHERE POC.VP_DOMAIN = (SELECT VP_DOMAIN
FROM ITIB_VPDOMAIN
WHERE ID = :NEW."VP_DOMAIN")
AND CAPEX_CATEGORY = :NEW.C_CAPEX_CATEGORY
AND :NEW.C_TOTAL_EURO <= 250 ;
END IF;
EXCEPTION
when no_data_found then
null ; -- do nothing; :NEW.J_PROJECT_ORACLE_CODE is already null
END BEFORE EACH ROW;
END AL_PROJECT_ORACLE_CODE_TRG;
/
I don't really see why this needs to be a compound trigger though, you aren't accessing the table the trigger is against (as you were in your previous question) and you only have a single scenario - only before insert; so a simple trigger would work here:
create or replace WS5108.TRIGGER AL_PROJECT_ORACLE_CODE_TRG
BEFORE INSERT
ON ITIB_REQUESTS
FOR EACH ROW
BEGIN
IF :NEW.J_PROJECT_ORACLE_CODE IS NULL THEN
SELECT PROJECT_ORACLE_CODE
INTO :NEW.J_PROJECT_ORACLE_CODE
FROM ITIB_PROJECT_ORACLE_CODE POC
JOIN ITIB_VPDOMAIN VP ON (POC.VP_DOMAIN = VP.VP_DOMAIN)
WHERE POC.VP_DOMAIN = (SELECT VP_DOMAIN
FROM ITIB_VPDOMAIN
WHERE ID = :NEW.VP_DOMAIN)
AND CAPEX_CATEGORY = :NEW.C_CAPEX_CATEGORY
AND :NEW.C_TOTAL_EURO <= 250 ;
END IF;
EXCEPTION
when no_data_found then
null ; -- do nothing; :NEW.J_PROJECT_ORACLE_CODE is already null
END AL_PROJECT_ORACLE_CODE_TRG;
/
And even the query within that looks confused; you don't need the subquery:
...
SELECT PROJECT_ORACLE_CODE
INTO :NEW.J_PROJECT_ORACLE_CODE
FROM ITIB_PROJECT_ORACLE_CODE POC
JOIN ITIB_VPDOMAIN VP ON (POC.VP_DOMAIN = VP.VP_DOMAIN)
WHERE VP.ID = :NEW.VP_DOMAIN
AND CAPEX_CATEGORY = :NEW.C_CAPEX_CATEGORY
AND :NEW.C_TOTAL_EURO <= 250 ;
...

Foreign key between two databases

I went through the previous answers to create a pseudo-foreign key to reference tables between two databases in Netbeans 8.1. This is the code I came up with,
DELIMITER //
CREATE OR REPLACE TRIGGER conf_track_FK
AFTER INSERT OR UPDATE on S26994437.track#FIT5148B
FOR EACH ROW
BEGIN
IF EXISTS(select * from inserted I where not exists (select * from
S1234567.conference#FIT5148A A where I.conf_id=A.conf_id))
RAISE_APPLICATION_ERROR(-20001,'Violation of pseudo-foreign key.');
ROLLBACK;
END IF;
END;
/
However, I encounter the following errors:
PLS-00103: Encountered the symbol ";" when expecting one of the following:
) with and or group having intersect minus start union where
connect
PLS-00103: Encountered the symbol "ROLLBACK" when expecting one of the following:
:= . ( % ;
The symbol ":=" was substituted for "ROLLBACK" to continue.
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
end not pragma final instantiable order overriding static
member constructor map
Create Trigger dbo.MyTableTrigger ON dbo.MyTable, After Insert, Update
As
Begin
If NOT Exists(select PK from OtherDB.dbo.TableName where PK in (Select FK from inserted) BEGIN
-- Handle the Referential Error Here
END
END
Try the below illustrated snippet. Hope it helps. And also any TRANSACTIONS like COMMIT / ROLLBACK cant be placed INSIDE Trigger unless its an Autonomous transaction but that too not a good idea as parent transaction is hidden to the trigger transaction. So even the Parent transaction fails the Automnomous transaction will be done.
CREATE OR REPLACE TRIGGER conf_track_FK AFTER
INSERT OR
UPDATE ON S26994437.track#FIT5148B --Remove DB link as it cant be used in Trigger
FOR EACH ROW
DECLARE
lv_cnt PLS_INTEGER;
BEGIN
SELECT COUNT(1)
INTO lv_cnt
FROM inserted I
WHERE NOT EXISTS
(SELECT 1 FROM S1234567.conference#FIT5148A A WHERE I.conf_id=A.conf_id
);
IF lv_cnt > 0 THEN
RAISE_APPLICATION_ERROR(-20001,'Violation of pseudo-foreign key.');
END IF;
END;
/

Oracle Trigger - update table B based on a condition of table A

I'm attempting to create an Oracle trigger which sets the values of a column on table B based on a select statement run within the trigger.
I want to be able to set the values of the 'is_active' column in table B to 'N' based on the select statement after an insert on table A has been executed.
My query is as follows:
CREATE OR REPLACE TRIGGER INACTIVE_STATE
AFTER INSERT ON
COMMENTS
FOR EACH ROW
DECLARE
inactive_id number;
BEGIN
SELECT distinct b.id
into inactive_id
from comments a,
modules b
where a.module_name=b.name
and a.type_id='FUNCTIONAL'
and a.module_id=b.id;
update modules
set is_active='N'
where ID=:inactive_id
END INACTIVE_STATE;
/
When I try and complpile this trigger, I get the following errors:
Error(15,1): PL/SQL: SQL Statement ignored
Error(17,10): PLS-00049: bad bind variable 'INACTIVE_ID'
Error(17,15): PL/SQL: ORA-00933: SQL command not properly ended
Error(19,1): PLS-00103: Encountered the symbol "/" when expecting one of the following: ( begin case declare end exception exit for goto if loop mod null pragma raise return select update while with <an identifier> <a double-quoted delimited-identifier> <a bind variable> << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge
It seems it doesn't like the update statement, or the bind variable isn't being parsed within this process.
If I seperate these statements into 2 commands (using var to handle the bind variable :inactive_id) it works as expected.
VAR INACTIVE_ID number
begin
select distinct b.id into :INACTIVE_ID
from comments a,
modules b
where a.module_name=b.name
and a.type_id='FUNCTIONAL'
and a.module_id=b.id;
end;
/
PL/SQL procedure successfully completed.
SQL>
SQL> update modules
set is_active='N'
where ID=:INACTIVE_ID
/
1 row updated.
Any ideas what I might be overlooking?
As Tony Andrews pointed out in the comments of the original post, I was incorrectly using a colon before the "inactive_id" variable in the where clause.
The correct code should have been:
CREATE OR REPLACE TRIGGER INACTIVE_STATE
AFTER INSERT ON
COMMENTS
DECLARE
inactive_id number;
BEGIN
SELECT distinct b.id
into inactive_id
from comments a,
modules b
where a.module_name=b.name
and a.type_id='FUNCTIONAL'
and a.module_id=b.id;
update modules
set is_active='N'
where ID=inactive_id
END INACTIVE_STATE;
/
Try using
PRAGMA AUTONOMOUS_TRANSACTION;
before begin