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

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

Related

MERGE with Oracle Stored Procedure - PL/SQL

I am trying to insert a records into my table CAT_BOM_ITEM from table TMP_BOM_STEEL8. If there are new records from the source I want the target table to be updated.
I have a procedure created and I am using a merge query inside it.
create or replace PROCEDURE SP_LOAD_CAT_BOM_MATERIALS AS
BEGIN
DELETE FROM BI_ODS.CAT_BOM_ITEM;
INSERT INTO BI_ODS.CAT_BOM_ITEM
(
ID_BOM_ITEM,
PT_PART,
PS_COMP,
IPD_PART,
SEPARADOR,
ORG_ID,
DB_ID,
LOADDATE
)
SELECT
A.ID_SEPARADOR,
A.PT_PART,
A.PS_COMP,
A.IPD_PART,
A.SEPARADOR,
A.ORG_ID,
A.DB_ID,
A.LOADDATE
FROM TMP_BOM_STEEL8 A;
COMMIT;
EXECUTE IMMEDIATE
'
Merge Into BI_ODS.CAT_BOM_ITEM B
USING
(SELECT
ID_SEPARADOR,
PT_PART,
PS_COMP,
IPD_PART,
SEPARADOR,
ORG_ID,
DB_ID,
LOADDATE
FROM TMP_BOM_STEEL8 ) A
ON (A.ID_SEPARADOR = B.ID_BOM_ITEM
AND A.DB_ID = B.DB_ID
AND A.ORG_ID = B.ORG_ID)
WHEN MATCHED THEN UPDATE SET
A.PT_PART = B.PT_PART
A.PS_COMP= B.PS_COMP
A.IPD_PART= B.IPD_PART
A.SEPARADOR = B.SEPARADOR
WHEN NOT MATCHED THEN INSERT (
ID_SEPARADOR,
PT_PART,
PS_COMP,
IPD_PART,
SEPARADOR,
ORG_ID,
DB_ID,
LOADDATE)
VALUES (
A.ID_SEPARADOR,
A.PT_PART,
A.PS_COMP,
A.IPD_PART,
A.SEPARADOR,
A.ORG_ID,
A.DB_ID,
A.LOADDATE);
';
COMMIT;
When i compile the procedure this is the error:
ORA-00933: SQL command not properly ended
ORA-06512: at "BI_ODS.SP_LOAD_CAT_BOM_MATERIALS", line 28
ORA-06512: at line 2
Can someone help in solving this.
Thanks in advance.
You need to:
Remove the ; semi-colon from the end of the string you are passing to EXECUTE IMMEDIATE;
Add commas at the end of each assignment in the UPDATE clause of the MERGE statement;
Swap the left- and right-terms in the UPDATE assignments as you are updating B from A (rather than vice versa);
Change INSERT ( ID_SEPARADOR, to INSERT ( ID_BOM_ITEM,; and
Add END; to terminate the stored procedure.
You also don't need to use EXECUTE IMMEDIATE and you shouldn't COMMIT in a stored procedure (as it prevents you from being able to ROLLBACK multiple statements; instead, use COMMIT in the PL/SQL block you are using to call the procedure that way you can control when the COMMIT occurs and can chain several procedures together and potentially roll them all back if needed).
db<>fiddle here
You should have an END statement at the end of your procedure, but I don't see anything else particularly wrong. You don't need EXECUTE IMMEDIATE and that seems to be the line it's objecting to, so try removing that.

Issue with trigger for archiving

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;
/

How can I make trigger in Oracle SQL to change rows in another table

I need to write a trigger in my SQL code that changes values in table A(Asortyment) that is connected with table B(Historia_Zamowien) with relation Many-Many. To connect A and B I use table C(Zamowienia_Asortyment).
How it looks like in relational model
I need to get to Asortyment.Dostepnosc through Zamowienia_Asortyment after INSERT ON Historia_Zamowien and change values to 0. I wrote some code that doesnt work and i have no idea what is wrong. Would you help?
CREATE TRIGGER "Zmiana_Dostepnosci_Po_Zamowieniu"
AFTER INSERT ON "Historia_Zamowien"
FOR EACH ROW
BEGIN
UPDATE "Asortyment"
SET tab1."Dostepnosc" = 0
FROM "Asortyment" tab1 JOIN "Zamowienia_Asortyment" tab2 ON tab1."ID_sprzetu" = tab2."ID_sprzetu"
JOIN inserted tab3 ON tab2."Numer_zamowienia" = tab3."Numer_zamowienia"
WHERE tab1."ID_sprzetu" = tab2."ID_sprzetu" AND tab2."Numer_zamowienia" = inserted."Numer_Zamowienia"
END;
/
After i run the code i get:
Error(1,5): PL/SQL: SQL Statement ignored
Error(3,5): PL/SQL: ORA-00933: SQL command not properly ended
Error(7): PLS-00103: Endountered 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 <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 json_exists json_value json_query json_object json_array
There are several issues with your SQL :
in Oracle you cannot use JOIN within an UPDATE ; I replaced it with a WHERE EXISTS correlated subquery
you have repeated conditions in JOINs and WHERE clause, I simplified that
to refer to the newly inserted row in Historia_Zamowien, use the :NEW keyword (you seem to use inserted)
Try :
CREATE TRIGGER "Zmiana_Dostepnosci_Po_Zamowieniu"
AFTER INSERT ON "Historia_Zamowien"
FOR EACH ROW
BEGIN
UPDATE "Asortyment" tab1 SET tab1."Dostepnosc" = 0
WHERE EXISTS (
SELECT 1
FROM "Zamowienia_Asortyment" tab2
WHERE tab2."ID_sprzetu" = tab1."ID_sprzetu"
AND tab2."Numer_zamowienia" = NEW."Numer_Zamowienia"
)
END
/

Select from table that does not exist

I have a question regarding ORACLE, I wrote a PLSQL CODE that checks if a table exists, if it exists then I select something from this table..pseudocode is like:
if (table exists)
Select from table where....
the problem is that I always get an error if the table does not exist, even if the if condition is never met and the select statement is never executed.
I think it is because my code is checked at compile time: "select from.." and then it prints an error if the table does not exist. How can I solve such an issue?.. here is how my code looks like (I used generic names):
DECLARE
v_table_exists NUMBER;
BEGIN
SELECT NVL(MAX(1), 0)
INTO v_table_exists
FROM ALL_TABLES
WHERE TABLE_NAME = 'TABLE_TEST';
IF v_table_exists = 1 THEN
INSERT INTO MY_TABLE(COLUMN1, COLUMN2, COLUMN3, COLUMN4)
SELECT 1234,
5678,
T.COLUMN_TEST1,
T.COLUMN_TEST2
FROM TABLE_TEST T
WHERE T.FLAG = 1;
END IF;
END;
The issue is exactly in the fact that your procedure con not be compiled as it refers to a non existing object; you may need some dynamic SQL for this; for example:
create or replace procedure checkTable is
vCheckExists number;
vNum number;
begin
-- check if the table exists
select count(1)
into vCheckExists
from user_tables
where table_name = 'NON_EXISTING_TABLE';
--
if vCheckExists = 1 then
-- query the table with dynamic SQL
execute immediate 'select count(1) from NON_EXISTING_TABLE'
into vNum;
else
vNum := -1;
end if;
dbms_output.put_line(vNum);
end;
The procedure compiles even if the table does not exist; if you call it now, you get:
SQL> select count(1) from NON_EXISTING_TABLE;
select count(1) from NON_EXISTING_TABLE
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL> exec checkTable;
-1
PL/SQL procedure successfully completed.
Then, if you create the table and call the procedure again:
SQL> create table NON_EXISTING_TABLE(a) as select 1 from dual;
Table created.
SQL> exec checkTable;
1
PL/SQL procedure successfully completed.
The same way I showed a SELECT, you can do an UPDATE or whatever SQL query you need; if you do something different from a SELECT, the INTO clause has to be removed.
For example, say you need to insert into a different table, the above code should be edited this way:
if vCheckExists = 1 then
execute immediate 'insert into target(a, b, c) select a, 1, 100 from NON_EXISTING_TABLE';
end if;
Everything will need to be done in Dynamic SQL (DBMS_SQL) or EXECUTE_IMMEDIATE otherwise your code will never compile (or package will be invalided) if table does not exists.
DBMS_SQL Example
EXECUTE_IMMEDIATE Example
According to this article, in Oracle Database Server static SQL is indeed checked at compile time to ensure referenced objects exist.
So I advise you to use dynamic SQL instead of static SQL, through a varchar for example.

Error(2,7): PLS-00428: an INTO clause is expected in this SELECT statement

I'm trying to create this trigger and getting the following compiler errors:
create or replace
TRIGGER RESTAR_PLAZAS
AFTER INSERT ON PLAN_VUELO
BEGIN
SELECT F.NRO_VUELO, M.CAPACIDAD, M.CAPACIDAD - COALESCE((
SELECT count(*) FROM PLAN_VUELO P
WHERE P.NRO_VUELO = F.NRO_VUELO
), 0) as PLAZAS_DISPONIBLES
FROM VUELO F
INNER JOIN MODELO M ON M.ID = F.CODIGO_AVION;
END RESTAR_PLAZAS;
Error(2,7): PL/SQL: SQL Statement ignored
Error(8,5): PL/SQL: ORA-00933: SQL command not properly ended
Error(8,27): 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 <an identifier> <a double-quoted delimited-identifier> <a bind variable> << close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe
Error(2,1): PLS-00428: an INTO clause is expected in this SELECT statement
What's wrong with this trigger?
You won't be allowed to
SELECT count(*) FROM PLAN_VUELO
in a trigger on PLAN_VUELO
Don't use a trigger. Use a stored procedure.
Inside a PL/SQL block you have to SELECT ... INTO something. I had an example of this in an answer to one of your questions yesterday. In this case you may want to select into a local variable, and use the result to then update another table.
But it looks like you're probably going to get lots of results back because you haven't restricted to the value you're interested in; the WHERE clauses don't filter on any of the inserted row's :NEW values. That will cause an ORA-02112. You need to make sure your select will return exactly one row, or look at cursors if you actually want multiple rows.
Just add the into clause according to the result type, one example:
declare
my_result VUELO%rowtype;
begin
select v.* into my_result from VUELO v where id = '1';
end;