Cannot create trigger if-delete-clause - sql

Here is the code I have
CREATE TRIGGER free_tokens AFTER INSERT ON `csrf_token`
IF (SELECT COUNT(`csrf_token`.`id`)) > 5000 THEN
DELETE FROM `csrf_token` WHERE `csrf_token`.`time` < (UNIX_TIMESTAMP(NOW() - INTERVAL 2 HOUR))
END IF;
which checks if there is more than 5000 entries after an insert, then deletes all entries which are greater than 2 hours old
I am getting you have an error in your mysql syntax near IF (SELECT ... DELETE FROM ...
I am using MariaDB, can someone help me understand where is the error in this simple statement?

There are small things that your trigger definition is missing:
change the MariaDB delimiter type into '//', so that the trigger instructions can be separated by the semicolon instead
FOR EACH ROW, as part of MariaDB trigger syntax
your DELETE statement is missing a semicolon at the end
can't nest queries inside an IF-THEN-ELSE statement
END to delimit the end of the trigger after its definition, as part of MariaDB syntax
A workaround for the nested query in your IF statement can be to define a variable to be updated with the result of a SELECT, to be carried out before the IF statement.
DELIMITER //
CREATE OR REPLACE TRIGGER free_tokens
AFTER INSERT
ON `csrf_token`
FOR EACH ROW
BEGIN
DECLARE num_rows INT;
SELECT
COUNT(*) INTO num_rows
FROM
`csrf_token`;
IF num_rows > 5000 THEN
DELETE FROM
`csrf_token`
WHERE
`csrf_token`.`time` < (UNIX_TIMESTAMP(NOW() - INTERVAL 2 HOUR));
END IF;
END//
DELIMITER ;
More on MariaDB trigger syntax here.

Related

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
/

Oracle- Trigger Compiliation error

I am trying to create a trigger to check the month before inserting to the database. the followingg code was triedbut showing a complationng error as Warning: Trigger created with compilation errors.
this is the code
CREATE OR REPLACE TRIGGER tr_july
BEFORE INSERT
ON TBL_EVENT
BEGIN
SELECT EXTRACT(month FROM EVN_DATE) FROM TBL_EVENT;
IF EXTRACT (month from EVN_DATE) == 7 THEN
RAISE_APPLICATION_ERROR(-20110, 'NOT ALLOWED TO INSERT RECORDS DURING JULY');
END IF;
END;
/
I think you are looking for something like this:
CREATE OR REPLACE TRIGGER tr_july
BEFORE INSERT ON TBL_EVENT
BEGIN
IF EXTRACT (month from :new.EVN_DATE) = 7 THEN
RAISE_APPLICATION_ERROR(-20110, 'NOT ALLOWED TO INSERT RECORDS DURING JULY');
END IF;
END; /
Study the documentation, a section "Accessing Column Values in Row Triggers"
https://docs.oracle.com/cd/B19306_01/appdev.102/b14251/adfns_triggers.htm
to learn how to access columns of the current row within the trigger body.
In short: You need to use "correlation names" named NEW and OLD
Tip: run SET DEFINE OFF; before compiling the trigger to avoid bind variable substitution (variables prepended by a colon :).
Use it as:
CREATE OR REPLACE TRIGGER tr_july
BEFORE INSERT
ON TBL_EVENT
Declare
E_date date;
BEGIN
SELECT EVN_DATE into E_date FROM TBL_EVENT;
IF EXTRACT (month from E_date) = 7 THEN
RAISE_APPLICATION_ERROR(-20110, 'NOT ALLOWED TO INSERT RECORDS DURING JULY');
END IF;
END;
Note: Oracle if clause require = only once
There are multiple isues with this code.
First it is a before insert trigger, so the value you want to check is not yet in the table. You can't find it with a select.
Triggers are pl/sql code. Any value you select within a pl/sql procedure you have to 'select aaa INTO bbb from xxx; and bbb must be declared before your BEGIN.
In Oracle the equal comparison operator is a single = (not ==).
Within a trigger you have the special qualifiers :new and :old to reference the column values you are working on.
In update triggers only the :new qualifier is usable.
CREATE OR REPLACE TRIGGER tr_july
BEFORE INSERT
ON TBL_EVENT
BEGIN
IF EXTRACT (month from :new.EVN_DATE) = 7 THEN
RAISE_APPLICATION_ERROR(-20110, 'NOT ALLOWED TO INSERT RECORDS DURING JULY');
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

Assign update query value to variable in SQL

I need to insert some rows to tables in PostgreSQL. But before inserting the row into table, I should check whether the record is existing or not by using update statement. If update statement returns 0 rows, then I can insert, otherwise I can skip that insertion. I should write SQL statements for this in .sql file in PostgreSQL.
How to achieve this?
In Oracle we have below format:
declare
i number;
begin
update employees set status = 'fired' where name like '%Bloggs';
i := sql%rowcount;
IF i ==0 THEN
insert statement here
end
How can I achieve this in Postgres?
If concurrency is not a problem for you, and you want to do it in a plpgsql function, rather use the special variable FOUND:
DO
$do$
BEGIN
UPDATE employees SET status = 'fired' WHERE ... ;
IF NOT FOUND THEN
-- insert statement here
END IF;
END
$do$
Or use a data-modifying CTE:
Insert if not exists else update it in Netezza
Optimize INSERT / UPDATE / DELETE operation
If concurrency is relevant, read up on proper UPSERT solutions.
How to UPSERT (MERGE, INSERT ... ON DUPLICATE UPDATE) in PostgreSQL?