Trigger after update: How to execute query for every row updated - triggers

I wrote trigger to execute a query after the update of the column (retard) in my table, but sometimes there are many rows updated how to solve that?
CREATE OR ALTER TRIGGER notifRetard
ON Taches
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #value INT
IF UPDATE(retard)
-- How to make this for every row updated???
SELECT
#value = inserted.retard
FROM
inserted;
IF #value = 1
-- run SQL query
END

The solution if someone else need it is to use CURSOR.
CREATE or alter TRIGGER notifRetard
ON Taches
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF UPDATE(retard)
begin
DECLARE #RefTache varchar(50),#RefPhase numeric(4,0),#IDprojet varchar(50),#IDressource varchar(50) #retard bit;
DECLARE TrigTempUpdate_Cursor CURSOR FOR
SELECt RefTache,RefPhase,IDprojet,IDressource,retard
FROM
inserted;
begin
OPEN TrigTempUpdate_Cursor;
FETCH NEXT FROM TrigTempUpdate_Cursor INTO #RefTache, #RefPhase,#IDprojet,#IDressource,#retard
WHILE ##FETCH_STATUS = 0
BEGIN
if #retard=1
--DO QUERY HERE
FETCH NEXT FROM TrigTempUpdate_Cursor INTO #RefTache, #RefPhase,#IDprojet,#IDressource,#retard
END;
end;
CLOSE TrigTempUpdate_Cursor;
DEALLOCATE TrigTempUpdate_Cursor;
end;
end;

Related

Set value using select

I am new to using postgresql, I am trying to make a trigger that just inserts in the Employee table and also inserts in the Vacations table, but I don't know how to assign the values, I do it like that in sql but here I really don't know how
CREATE FUNCTION SP_InsertaVacacionesEmpleado() RETURNS TRIGGER
AS
$$
DECLARE _NumeroIdentificacion INTEGER;
DECLARE _FechaEntrada DATE;
BEGIN
SET _NumeroIdentificacion = SELECT NEW.NumeroIdentificacion FROM "Empleado"
SET _FechaEntrada = SELECT NEW.FechaEntrada FROM "Empleado"
INSERT INTO Vacaciones VALUES(_NumeroIdentificacion, _FechaEntrada, '', 0);
RETURN NEW;
END
$$
LANGUAGE plpgsql
As documented in the manual assignment is done using the := operator, e.g.:
some_variable := 42;
However to assign one or more variables from the result of a query, use select into, e.g.:
DECLARE
var_1 INTEGER;
var_2 DATE;
BEGIN
select col1, col2
into var_1, var_2
from some_table
...
However neither of that is necessary in a trigger as you can simply use the reference to the NEW record directly in the INSERT statement:
CREATE FUNCTION sp_insertavacacionesempleado()
RETURNS TRIGGER
AS
$$
BEGIN
INSERT INTO Vacaciones (...)
VALUES (NEW.NumeroIdentificacion, NEW.FechaEntrada , '', 0);
RETURN NEW;
END
$$
LANGUAGE plpgsql;
Note that you need to define a row level trigger for this to work:
create trigger ..
before insert on ...
for each row --<< important!
execute procedure sp_insertavacacionesempleado() ;

How to set a trigger in PostgreSQL?

I have the tables show here -> How can I display if an airline departs from all the airport in my DB in Postgresql?
I've update the airlines table like below
ALTER TABLE airlines
ADD COLUMN count_flight INTEGER;
Now I need to update the column count_flight with the number of flight for each airline that are already stored in volo table.
How can I set a trigger that do what I need + update the value of count_flight every time I insert a new row in volo table?
EDIT
I've set a trigger like below and it works
CREATE FUNCTION update_flight() RETURNS TRIGGER AS $updateN_voli$
DECLARE
airline_name varchar;
BEGIN
IF (TG_OP = 'INSERT') THEN
airline_name = NEW.airline;
UPDATE airlines
SET count_flight = count_flight + 1
WHERE airline_name = airlines.airline_name;
END IF;
RETURN NULL;
END;
$updateN_voli$ LANGUAGE plpgsql;
/* Creo il trigger */
CREATE TRIGGER update_flight
AFTER INSERT ON volo
FOR EACH ROW
EXECUTE PROCEDURE update_flight();
Now I can't figure out how can I update the value with the existing flight. As I said in the comment below I have found a possible solution but is a bit complex if I have a big amount of row in table volo.
I've found the answer:
This is for update the column with existing data in volo table:
DO
$$
DECLARE
a varchar;
BEGIN
FOR a IN select airline from airlines LOOP
UPDATE airlines
SET count_flight = (SELECT COUNT (airline) FROM volo WHERE airline = a)
WHERE airline = a;
END LOOP;
END;
$$;
This is for update te airlines table every time a new flight is add to volo table:
CREATE FUNCTION update_newVoli() RETURNS TRIGGER AS $updateN_voli$
DECLARE
name_airline varchar;
BEGIN
IF (TG_OP = 'INSERT') THEN
name_airline = NEW.airline;
UPDATE compagnie
SET count_flight = count_flight + 1
WHERE name_airline = airlines.airline;
END IF;
RETURN NULL;
END;
$updateN_voli$ LANGUAGE plpgsql;
CREATE TRIGGER update_newVoli
AFTER INSERT ON volo
FOR EACH ROW
EXECUTE PROCEDURE update_newVoli();

How To Restrict Delete using PL/pgSQL trigger?

If the client user is trying to delete more than 5 records from a Table i want to restrict that using a trigger. I have a basic idea to do that but i don't know how to implement the Idea. I appreciate any HELP.
Basic Idea : In Trigger IF TG_OP = Delete and the count of records to be deleted are more than 5 then Restrict.
CREATE TRIGGER adjust_count_trigger BEFORE DELETE ON schemaname.tablename
FOR EACH ROW EXECUTE PROCEDURE public.adjust_count();
CREATE OR REPLACE FUNCTION adjust_count()
RETURNS TRIGGER AS
$$
DECLARE
num_rows int;
num_rows1 int;
BEGIN
IF TG_OP = 'DELETE' THEN
EXECUTE 'select count(*) from '||TG_TABLE_SCHEMA ||'.'||TG_RELNAME ||' where oid = old.oid ' into num_rows ;
IF num_rows > 5 Then
RAISE NOTICE 'Cannot Delete More than 5 Records , % ', num_rows ;
END IF ;
END IF ;
RETURN OLD;
END;
$$
LANGUAGE 'plpgsql';
In earlier versions of Postgres you can simulate a transition table introduced in Postgres 10. You need two triggers.
create trigger before_delete
before delete on my_table
for each row execute procedure before_delete();
create trigger after_delete
after delete on my_table
for each statement execute procedure after_delete();
In the first trigger create a temp table and insert a row into it:
create or replace function before_delete()
returns trigger language plpgsql as $$
begin
create temp table if not exists deleted_rows_of_my_table (dummy int);
insert into deleted_rows_of_my_table values (1);
return old;
end $$;
In the other trigger count rows of the temp table and drop it:
create or replace function after_delete()
returns trigger language plpgsql as $$
declare
num_rows bigint;
begin
select count(*) from deleted_rows_of_my_table into num_rows;
drop table deleted_rows_of_my_table;
if num_rows > 5 then
raise exception 'Cannot Delete More than 5 Records , % ', num_rows;
end if;
return null;
end $$;
The above solution may seem a bit hacky but it is safe if only the temp table does not exist before delete (do not use the same name of the temp table for multiple tables).
Test it in rextester.
You can easily do that with the new transition relation feature from PostgreSQL v10:
CREATE OR REPLACE FUNCTION forbid_more_than() RETURNS trigger
LANGUAGE plpgsql AS
$$DECLARE
n bigint := TG_ARGV[0];
BEGIN
IF (SELECT count(*) FROM deleted_rows) <= n IS NOT TRUE
THEN
RAISE EXCEPTION 'More than % rows deleted', n;
END IF;
RETURN OLD;
END;$$;
CREATE TRIGGER forbid_more_than_5
AFTER DELETE ON mytable
REFERENCING OLD TABLE AS deleted_rows
FOR EACH STATEMENT
EXECUTE PROCEDURE forbid_more_than(5);

rollback a nested transaction in trigger

I have a following situation.
I have a table with trigger for insert.
When I insert a row in it, from this trigger I want to insert some rows into a second table.
For each of these rows I want to do it in it's own transaction in case something go wrong.
I want to have original row in first table and all rows (these withous errors) in the second.
A little code to reproduce:
create table test(id int primary key identity(1,1),name nvarchar(10))
create table test2(id int primary key identity(1,1),
state char(1) check (state in ('a','b')))
go
create trigger test_trigger on test for insert
as
begin
declare #char char(1)
declare curs cursor for (
select 'a'
union
select 'c'
union
select 'b')
open curs
fetch next from curs into #char
while ##FETCH_STATUS = 0
begin
begin try
begin transaction
insert into test2(state)
select #char
commit
end try
begin catch
print 'catch block'
rollback
end catch
fetch next from curs into #char
end
close curs
deallocate curs
end
go
insert into test(name) values('test')
select * from test
select * from test2
go
drop table test
drop table test2
So for the sample data from this snippet, I want to have a row with 'test' in test table and two rows in the test2 table ('a' and 'b').
How can I write a code for that?
Looks like finally I got it to work.
Corrected trigger code:
create trigger test_trigger on test for insert
as
begin
declare #char char(1)
set xact_abort off
declare curs cursor for (
select 'a'
union
select 'c'
union
select 'b')
open curs
fetch next from curs into #char
while ##FETCH_STATUS = 0
begin
save transaction t
begin try
insert into test2(state)
select #char
end try
begin catch
print 'catch block'
rollback transaction t
end catch
fetch next from curs into #char
end
close curs
deallocate curs
end

HSQLDB DECLARE ROW IN A TRIGGER

Hi all I try to get a row form my select statemnt in a trigger but I got this error unexpected token: R
CREATE TRIGGER PUBLIC.TRIGGERNAME AFTER UPDATE ON PUBLIC.CLIENTE_OFFERENTE
REFERENCING NEW ROW AS NUOVO
FOR EACH ROW
BEGIN ATOMIC
DECLARE r RECORD;
SET r=(SELECT offerta.IDIMMOBILE as ID, immobile.prezzomax as costo FROM PUBLIC.OFFERTA join immobile on immobile.idImmobile=offerta.idImmobile WHERE IDOFFERTA=NUOVO.IDOFFERTA);
IF NUOVO.STATO='Venduto'THEN
INSERT INTO PUBLIC.VENDITE(IDCLIENTE,IDIMMOBILE,COSTO)VALUES(NUOVO.IDCLIENTE,r.ID,r.costo);
END IF;
END
You cannot DECLARE a variable of RECORD type.
You want to get two values from your select statement. Therefore you need to declare two variables.
BEGIN ATOMIC
DECLARE VAR_ID INT;
DECLARE VAR_COSTO INT;
SET (VAR_ID, VAR_COSTO) =(SELECT offerta.IDIMMOBILE as ID, immobile.prezzomax as costo FROM PUBLIC.OFFERTA join immobile on immobile.idImmobile=offerta.idImmobile WHERE IDOFFERTA=NUOVO.IDOFFERTA);
IF NUOVO.STATO='Venduto'THEN
INSERT INTO PUBLIC.VENDITE(IDCLIENTE,IDIMMOBILE,COSTO)VALUES(NUOVO.IDCLIENTE,VAR_ID,VAR_COSTO);
END IF;
END