How to use when condition with subquery like "NOT IN" with trigger? - sql

this is my code and i don't know what wrong this:
CREATE TRIGGER trigger_before_insert_instruments
BEFORE INSERT ON Instruments
FOR EACH ROW
WHEN (NEW.type NOT IN (SELECT type FROM Types))
EXECUTE PROCEDURE
insert_new_type_or_famille();
But the console return me an error like "you can use subquery in the when condition...

Use if in the body of the trigger. That would look something like this:
CREATE TRIGGER trigger_before_insert_instruments
BEFORE INSERT ON Instruments
FOR EACH ROW
BEGIN
IF NOT EXISTS (SELECT 1 FROM Types WHERE :NEW.type = t.Types) THEN
EXECUTE PROCEDURE insert_new_type_or_famille();
END IF;
END;

Related

How to use variables in PostgreSQL transaction

How in Postgresql inside a transaction to get values ​​into a variable, and if SELECT did not return anything, throw an error, and if SELECT returned data, then use them in the transaction?
Like this:
BEGIN;
#activeRounds = SELECT * FROM "rounds" WHERE status = 'active';
if(!#activeRounds) {
RAISE ERROR "Has no Active rounds"
};
INSERT INTO "bet"
(user_id, round_id)
VALUES
(100, #activeRound[0].id)
COMMIT;
how to do something similar in one request within a transaction?
You can adapt following code to your table structure:
begin;
do
$$
declare
v int;
begin
select c1 into v from t1 where k1=1;
if not found
then
raise exception 'no row found';
else
insert into t2(c2) values(v);
end if;
end;
$$;
commit;
Note the difference bewteen begin; to start a transaction and begin to start a pl/pgSQL block.
Write a DO statement in PL/pgSQL.
Like every SQL statement, DO runs in a single transaction. PL/pgSQL is a procedural language that has variables and conditional processing.

Cannot to create trigger properly

Oracle apex, PL_Sql.
This is my trigger:
CREATE OR REPLACE TRIGGER NoMoreThanOneHorse
BEFORE INSERT OR UPDATE OF Jockey_ID
ON Horses
FOR EACH ROW
DECLARE
NumOfHorsesForJockey NUMBER(4);
BEGIN
SELECT COUNT(*) INTO NumOfHorsesForJockey FROM Horses
WHERE Jockey_ID = :NEW.Jockey_ID AND Horse_ID <> :NEW.Horse_ID;
IF NumOfHorsesForJockey > 0
THEN RAISE_APPLICATION_ERROR (-20445, 'Нельзя закрепить за лошадью уже занятого жокея!');
END IF;
END NoMoreThanOneHorse;
but every time i try to create it i have this error :ORA-24344: success with compilation error ORA-06512. I suppose it's because bad syntax
I don't know what the compile-time error might be - it compiles fine for me. But at run-time you'll probably get an `ORA-04091 Table HORSES is mutating, trigger cannot see it" error. You can't fetch from HORSES in a ROW trigger which is defined on the HORSES table.
What you need to do is to change this to an AFTER STATEMENT trigger, which you do by leaving off the FOR EACH ROW in the trigger definition, and changing the trigger point from BEFORE to AFTER. The problem is that in a statement trigger you don't have access to the :NEW or :OLD data, but you don't really need it. A statement trigger is invoked only once for each statement which is executed. So your trigger should look something like:
CREATE OR REPLACE TRIGGER NoMoreThanOneHorse
AFTER INSERT OR UPDATE OF Jockey_ID
ON Horses
DECLARE
nMaxHorses NUMBER;
BEGIN
SELECT MAX(HORSE_COUNT)
INTO nMaxHorses
FROM (SELECT JOCKEY_ID, COUNT(*) AS HORSE_COUNT
FROM Horses
GROUP BY Jockey_ID);
IF nMaxHorses > 1 THEN
RAISE_APPLICATION_ERROR (-20445, 'Нельзя закрепить за лошадью уже занятого жокея!');
END IF;
END NoMoreThanOneHorse;
You really don't care which jockey has been assigned too many horses, only that there is a jockey who's been put on more than one horse.
db<>fiddle here

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

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

sql query inside if stage with exists

I want to check if the id I want to insert into tableA exists in tableB into an if statement
Can I do something like this
if new.id exists (select id from tableB where stat = '0' ) then
some code here
end if;
When I try this I get an error message, any thoughts?
Why not do it like this? I'm not very knowledgeable about PostgreSQL but this would work in T-SQL.
INSERT INTO TargetTable(ID)
SELECT ID
FROM TableB
WHERE ID NOT IN (SELECT DISTINCT ID FROM TargetTable)
This is usually done with a trigger. A trigger function does the trick:
CREATE FUNCTION "trf_insert_tableA"() RETURNS trigger AS $$
BEGIN
PERFORM * FROM "tableB" WHERE id = NEW.id AND stat = '0';
IF FOUND THEN
-- Any additional code to go here, optional
RETURN NEW;
ELSE
RETURN NULL;
END IF;
END; $$ LANGUAGE plpgsql;
CREATE TRIGGER "tr_insert_tableA"
BEFORE INSERT ON "tableA"
FOR EACH ROW EXECUTE PROCEDURE "trf_insert_tableA"();
A few notes:
Identifiers in PostgreSQL are case-insensitive. PostgreSQL by default makes them lower-case. To maintain the case, use double-quotes. To make your life easy, use lower-case only.
A trigger needs a trigger function, this is always a two-step affair.
In an INSERT trigger, you can use the NEW implicit parameter to access the column values that are attempted to be inserted. In the trigger function you can modify these values and those values are then inserted. This only works in a BEFORE INSERT trigger, obviously; AFTER INSERT triggers are used for side effects such as logging, auditing or cascading inserts to other tables.
The PERFORM statement is a special form of a SELECT statement to test for the presence of data; it does not return any data, but it does set the FOUND implicit parameter that you can use in a conditional statement.
Depending on your logic, you may want the insert to succeed or to fail. RETURN NEW to make the insert succeed, RETURN NULL to make it fail.
After you defined the trigger, you can simply issue an INSERT statement: the trigger function is invoked automatically.
Presumably, you want something like this:
if exists (select 1 from tableB b where stat = '0' and b.id = new.id) then
some code here
end if;