Syntax Error on CREATE TRIGGER and CREATE PROCEDURE - postgresql

I am trying to create a FUNCTION, TRIGGER, and PROCEDURE. Each time I try to run it, I get a Syntax Error on/around the CREATE line. Below is what I have:
------------------------------------------
CREATE FUNCTION summary_refresh_fun()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
DELETE FROM summary;
INSERT INTO summary (
SELECT
concat_ws (', ', last_name, first_name) AS customer_name,
email,
COUNT(customer_id)
FROM detailed
GROUP BY customer_id, customer_name, email
-- HAVING count(customer_id) > 30
ORDER BY count(customer_id)DESC
LIMIT 100
);
RETURN NEW;
END; $$
----------------------------------------------
CREATE TRIGGER summary_refresh
AFTER INSERT ON detailed
FOR EACH STATEMENT
EXECUTE PROCEDURE summary_refresh_fun();
---------------------------------------------
CREATE PROCEDURE refresh_tables()
LANGUAGE plpgsql
AS $$
BEGIN
DELETE FROM detailed;
INSERT INTO detailed(
customer_id, -- customer
first_name, -- customer
last_name, -- customer
email, -- customer
rental_id, -- rental
rental_date, -- rental
return_date, -- rental
staff_id --rental
)
SELECT
c.customer_id, c.first_name, c.last_name, c.email,
r.rental_id, r.rental_date, r.return_date, r.staff_id
FROM rental AS r
INNER JOIN customer AS c ON c.customer_id = r.customer_id;
END;$$
I am getting errors at CREATE TRIGGER and CREATE PROCEDURE:
[Syntax Error on CREATE TRIGGER][1]
[Syntax Error on CREATE PROCEDURE][2]
Any idea on why this Syntax Error is occurring?
Using PostgreSQL 13
[1]: https://i.stack.imgur.com/TIyPP.png
[2]: https://i.stack.imgur.com/gL5ya.png

You need ; after END; $$ to separate create function from create trigger

Related

How do I update a summary table with a trigger?

I am working with the sample DVD_Rental database. I have to create a trigger on my category_performance_details table that will create a summary table every time data is added to the detail table.
These are the queries I am using to setup my tables:
DROP TABLE IF EXISTS category_performance_summary;
CREATE TABLE category_performance_summary (
genre VARCHAR(25),
total_sales numeric
);
DROP TABLE IF EXISTS category_performance_details;
CREATE TABLE if not exists category_performance_details (
category_id Int,
category_name VARCHAR(25),
film_id Int,
film_title VARCHAR(255),
film_rental_rate numeric(4,2),
inventory_id Int,
payment_id Int,
payment_amount numeric(4,2),
rental_id Int,
rental_date Timestamp
);
I want to update the summary table after every insert statement with a trigger so that it provides a summary of the total sales per category. Basically, the summary table should be the same result as:
SELECT category_name, SUM(payment_amount) FROM category_performance_details
GROUP BY category_name;
This is my procedure and trigger. For some reason, I am getting a syntax error at or near "CREATE TRIGGER". Am I approaching this trigger correctly? What is wrong with my syntax?
CREATE OR REPLACE FUNCTION update_summary_table()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
BEGIN
TRUNCATE TABLE category_performance_summary;
INSERT INTO category_performance_summary (genre, total_sales)
SELECT category_name, SUM(payment_amount)
FROM category_performance_details
GROUP BY category_name;
RETURN NEW;
END;
$$
CREATE TRIGGER update_summary
AFTER INSERT
ON category_performance_summary
FOR EACH STATEMENT
EXECUTE PROCEDURE update_summary_table();
You can also simply increment or decrement the total_sales in the trigger function.
In any case you have to use the NEW (resp. OLD) key word in order to refer to the values that are inserted/updated (resp. deleted) in table category_performance_summary :
CREATE OR REPLACE FUNCTION increment_summary_table()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
BEGIN
INSERT INTO category_performance_summary (genre, total_sales)
VALUES (NEW.category_name, 1)
ON CONFLICT (genre) DO UPDATE
SET total_sales= total_sales + 1
WHERE genre= NEW.category_name ;
RETURN NEW;
END;
$$ ;
CREATE TRIGGER increment_summary
AFTER INSERT
ON category_performance_summary
FOR EACH STATEMENT
EXECUTE PROCEDURE increment_summary_table();
CREATE OR REPLACE FUNCTION decrement_summary_table()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
BEGIN
UPDATE category_performance_summary
SET total_sales= total_sales - 1
WHERE genre= OLD.category_name ;
RETURN OLD;
END;
$$ ;
CREATE TRIGGER decrement_summary
AFTER DELETE
ON category_performance_summary
FOR EACH STATEMENT
EXECUTE PROCEDURE decrement_summary_table();

How to create with condition and select?

I want to do something like this :
Someone creates a customer and if the ct already exist so we check if the cn is the same and if it's not the same we raise an error but it' doesn't work and take a lot of time.
CREATE OR REPLACE FUNCTION existingCT()
RETURNS trigger AS $$ BEGIN
IF ((SELECT COUNT(*) FROM customer WHERE ct= NEW.ct)!= 0) THEN 
IF( (SELECT COUNT(*) FROM customer WHERE ct= NEW.ct) != (SELECT count(*) FROM customer WHERE ct= NEW.ct AND cn= NEW.cn)) THEN
RAISE EXCEPTION 'This ct already exist for a cn';
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql ;
You can look for a customer with the same ct and a different cn in one query:
IF EXISTS (SELECT * FROM customer WHERE ct = NEW.ct AND cn <> NEW.cn) THEN
A thrown exception should abort the INSERT for any type of trigger. But given that it's a check, I'd create the trigger as BEFORE INSERT.

Cannot delete a row when the trigger is enabled

Help Needed. i have a table and the respective audit table in emp schema.I was not able to delete the entry from the source table when the trigger is enabled.
The table is mapped to a trigger as stated below.
Below is the generic function , which i have used to audit across all the tables.
Function:
============
CREATE OR REPLACE FUNCTION emp.fn_entry_audit()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
declare
col_name text:='';
audit_table_name text := TG_TABLE_NAME || '_audit';
begin
if TG_OP = 'UPDATE' or TG_OP = 'INSERT' THEN
EXECUTE format('INSERT INTO emp.%1$I SELECT ($1).*,'''||TG_OP||'''',audit_table_name) using NEW;
else
EXECUTE format('INSERT INTO emp.%1$I SELECT ($1).*,'''||TG_OP||'''',audit_table_name) using old;
end if;
return new;
END $function$
Trigger creation
=================
create trigger trig_anish before insert or delete or update on emp.test_empname for each row execute procedure acaas.fn_entry_audit()
Table
======
create table emp.test_empname(id int4,fname varchar(300) not null,lname varchar(400),salary int4,last_modified_dt timestamp);
create table emp.test_empname_audit(id int4,fname varchar(300) not null,lname varchar(400),salary int4,last_modified_dt timestamp,modified_type varchar(10));
The difference between the two is modified_type column, which will mention whether the data is of insert, update or delete(TG_OP from above function).
now when i insert the values in emp.test_empname, it is getting inserted correctly in emp.test_empname_audit.
select * from emp.test_empname;
emp.test_empname:
==================
id fname lname salary last_modified_dt
===============================================================
1 stacker pentacost 1000 04-04-18
2 lyri pav 2000 04-04-18
3 TEST TEST1 1000 04-04-18
select * from emp.test_empname_audit;
id fname lname salary last_modified_dt modified_type
===============================================================
1 stacker pentacost 1000 04-04-18 INSERT
2 lyri pav 1000 04-04-18 INSERT
2 lyri pav 2000 04-04-18 UPDATE
3 TEST TEST1 1000 04-04-18 Delete
Now, the issue is whenever I perform delete on source table (test_empname), the query is executing fine, but it shows 0 rows affected.
when i query in the table select * from test_empname where id=3, it still exists.But you can see the entry in audit as delete.
I disabled the trigger and performed delete function ,it executes fine and the row gets affected.How is the trigger affecting my delete functionality.Please help!!
CREATE OR REPLACE FUNCTION acaas.fn_entry_audit1()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
declare
col_name text:='';
audit_table_name text := TG_TABLE_NAME || '_audit';
begin
if TG_OP = 'UPDATE' or TG_OP = 'INSERT' THEN
EXECUTE format('INSERT INTO acaas.%1$I SELECT ($1).*,'''||TG_OP||'''',audit_table_name) using NEW;
return new;
else
EXECUTE format('INSERT INTO acaas.%1$I SELECT ($1).*,'''||TG_OP||'''',audit_table_name) using old;
return old;
end if;
END $function$

Rewrite Oracle Procedure and trigger for SQL Server

I have an Oracle procedure to add a row to the JOB_HISTORY table and a trigger to call the procedure when data is updated on two columns( job_id, department_id) in the table EMPLOYEES: I´m trying to rewrite them for Sql server 2008, can anyone help me to rewrite both of them please? I might have done it with the procedure but cannot do it with the trigger. any suggestion is welcome?
Procedure:
CREATE OR REPLACE PROCEDURE add_job_history
( p_emp_id job_history.employee_id%type
, p_start_date job_history.start_date%type
, p_end_date job_history.end_date%type
, p_job_id job_history.job_id%type
, p_department_id job_history.department_id%type )
IS BEGIN
INSERT INTO job_history (employee_id, start_date, end_date,job_id,department_id)
VALUES(p_emp_id,p_start_date,p_end_date,p_job_id,p_department_id);
END add_job_history;
Trigger:
CREATE OR REPLACE TRIGGER update_job_history
AFTER UPDATE OF job_id,department_id ON employees
FOR EACH ROW
BEGIN
add_job_history(:old.employee_id, :old.hire_date, sysdate,
:old.job_id,:old.department_id);
END;
This is how I wrote the procedure I´m not sure is doing the same thing as the one above though.
CREATE PROCEDURE add_job_history
(#p_emp_id INTEGER,
#p_start_date DATE,
#p_end_date DATE,
#p_job_id VARCHAR(10),
#p_department_id INTEGER ) AS
BEGIN
INSERT INTO job_history (employee_id, start_date, end_date,
job_id, department_id)
VALUES(#p_emp_id, #p_start_date, #p_end_date, #p_job_id,#p_department_id)
END ;
CREATE TRIGGER update_job_history ON (table_EMPLOYEES)
AFTER UPDATE
AS
BEGIN
if exists(select 1 From inserted as i inner join deleted as d on d.employee_id = i.employee_id where d.job_id != i.job_id or d.department_id != i.department_id)
begin
insert into job_history (employee_id, start_date, end_date,job_id,department_id)
select d.employee_id, d.start_date,d.end_date,d.job_id,d.department_id
from deleted as d
end
END;
thanks for your help #Long
I found The answer even if I still have to test the logic of it and i might change some inserted i. with d.
create trigger update_job_history on employees
after update
as
begin
set nocount on;
insert into job_history (employee_id, start_date, end_date ,job_id,department_id)
select i.employee_id, i.hire_date as start_date , d.hire_date as end_date, d.job_id ,d.department_id
From inserted as i inner join deleted as d on d.employee_id = i.employee_id where d.job_id != i.job_id or d.department_id != i.department_id
end

Postgresql Trigger to insert rows

I'm stuck for days with triggers on Postgresql (and Mysql as well). I just want to insert newly filled rows to another table. The original data comes from an external form (OpenDataKit) and goes to "intermediate" tables. I can't understand why the form cannot send the data anymore once the trigger is created... Note that all actions work without the trigger, when I do the insertions by hand. I would greatly appreciate some help to understand what I am doing wrong. I am now testing with Postgresql 9.5, but I got similar issue with MySQL 5.1.
-- CREATE procedure:
CREATE OR REPLACE FUNCTION proc_natobs() RETURNS TRIGGER AS
$BODY$
DECLARE
BEGIN
INSERT INTO lieu (id_lieu, wgs_lat, wgs_lon, date_obs, geom)
SELECT id_loc,"GPS_TEL_LAT", "GPS_TEL_LNG", "DATE_OBS", ST_SetSRID(ST_POINT("GPS_TEL_LNG","GPS_TEL_LAT"), 4326)
FROM "FORMULAIRE_NATOBS_REPEAT_LOC", "FORMULAIRE_NATOBS_CORE"
WHERE "FORMULAIRE_NATOBS_CORE"."_URI" = "FORMULAIRE_NATOBS_REPEAT_LOC"."_TOP_LEVEL_AURI"
AND "FORMULAIRE_NATOBS_REPEAT_LOC".id_loc IN (SELECT max(id_loc) FROM "FORMULAIRE_NATOBS_REPEAT_LOC");
INSERT INTO i_lieu_observateurs (id_lieu, id_auteur)
SELECT id_loc, CAST("AUTEUR" AS integer)
FROM "FORMULAIRE_NATOBS_CORE", "FORMULAIRE_NATOBS_REPEAT_LOC"
WHERE "FORMULAIRE_NATOBS_REPEAT_LOC"."_TOP_LEVEL_AURI" = "FORMULAIRE_NATOBS_CORE"."_URI"
AND id_loc IN (SELECT max(id_loc) FROM "FORMULAIRE_NATOBS_REPEAT_LOC")
UNION
SELECT id_loc, CAST("OBSERVATEURS" AS integer)
FROM "FORMULAIRE_NATOBS_REPEAT_LOC", "FORMULAIRE_NATOBS_REPEAT_OBSERVATEUR"
WHERE "FORMULAIRE_NATOBS_REPEAT_LOC"."_TOP_LEVEL_AURI" = "FORMULAIRE_NATOBS_REPEAT_OBSERVATEUR"."_TOP_LEVEL_AURI"
AND id_loc IN (SELECT max(id_loc) FROM "FORMULAIRE_NATOBS_REPEAT_LOC")
;
END;
$BODY$
LANGUAGE 'plpgsql';
-- CREATE the trigger:
CREATE TRIGGER trigger_natobs AFTER INSERT
ON "FORMULAIRE_NATOBS_REPEAT_LOC"
FOR EACH ROW
EXECUTE PROCEDURE proc_natobs();
So, when the ODK form inserts new rows in FORMULAIRE_NATOBS_REPEAT_LOC (for which I have created a serial ID to facilitate the SQL queries), I try to insert this row (combined with information from other intermediate tables) into table "lieu" for the first action of the trigger, and into table i_lieu_observation (composed by a double primary key) for the second action. I tested also with a trigger composed by the first action only, but it does not work either. The Android app that sends the form crashes until I remove the trigger.
Thanks in advance!
You need to use the special NEW variable in the trigger to access the newly inserted data. So you need something like:
CREATE OR REPLACE FUNCTION proc_natobs() RETURNS TRIGGER AS
$BODY$
DECLARE
BEGIN
INSERT INTO lieu (id_lieu, wgs_lat, wgs_lon, date_obs, geom)
SELECT new.id_loc,"GPS_TEL_LAT", "GPS_TEL_LNG", "DATE_OBS", ST_SetSRID(ST_POINT("GPS_TEL_LNG","GPS_TEL_LAT"), 4326)
FROM "FORMULAIRE_NATOBS_CORE"
WHERE "FORMULAIRE_NATOBS_CORE"."_URI" = new."_TOP_LEVEL_AURI";
INSERT INTO i_lieu_observateurs (id_lieu, id_auteur)
SELECT new.id_loc, CAST("AUTEUR" AS integer)
FROM "FORMULAIRE_NATOBS_CORE"
WHERE new."_TOP_LEVEL_AURI" = "FORMULAIRE_NATOBS_CORE"."_URI"
UNION
SELECT new.id_loc, CAST("OBSERVATEURS" AS integer)
FROM "FORMULAIRE_NATOBS_REPEAT_OBSERVATEUR"
WHERE new."_TOP_LEVEL_AURI" = "FORMULAIRE_NATOBS_REPEAT_OBSERVATEUR"."_TOP_LEVEL_AURI";
RETURN new;
END;
$BODY$
LANGUAGE 'plpgsql';
-- CREATE the trigger:
CREATE TRIGGER trigger_natobs AFTER INSERT
ON "FORMULAIRE_NATOBS_REPEAT_LOC"
FOR EACH ROW
EXECUTE PROCEDURE proc_natobs();
Because I don't know which fields come from which tables, I cannot make the above totally correct. In the same way as I have written new.id_loc, you will need to put new.field_name for all fields coming from the formulaire_natobs_repeat_loc table.
HTH
Try this
CREATE OR REPLACE FUNCTION proc_natobs() RETURNS TRIGGER AS
$BODY$
BEGIN
IF(TG_OP = 'INSERT') THEN
INSERT INTO lieu (id_lieu, wgs_lat, wgs_lon, date_obs, geom)
SELECT id_loc,"GPS_TEL_LAT", "GPS_TEL_LNG", "DATE_OBS", ST_SetSRID(ST_POINT("GPS_TEL_LNG","GPS_TEL_LAT"), 4326)
FROM "FORMULAIRE_NATOBS_REPEAT_LOC" loc, "FORMULAIRE_NATOBS_CORE" core
WHERE core."_URI" = loc."_TOP_LEVEL_AURI"
AND loc.id_loc =new.id_loc;
INSERT INTO i_lieu_observateurs (id_lieu, id_auteur)
SELECT id_loc as id,
CAST("AUTEUR" AS integer) as auteur
FROM "FORMULAIRE_NATOBS_CORE" core, "FORMULAIRE_NATOBS_REPEAT_LOC" loc
WHERE loc."_TOP_LEVEL_AURI" = core."_URI"
AND loc.id_loc =new.id_loc;
UNION
SELECT id_loc as id,
CAST("OBSERVATEURS" AS integer) as auteur
FROM "FORMULAIRE_NATOBS_REPEAT_LOC" loc, "FORMULAIRE_NATOBS_REPEAT_OBSERVATEUR" obs
WHERE loc."_TOP_LEVEL_AURI" = obs."_TOP_LEVEL_AURI"
AND loc.id_loc =new.id_loc;
END IF;
Return new;
END;
$BODY$
LANGUAGE 'plpgsql';
-- CREATE the trigger:
CREATE TRIGGER trigger_natobs AFTER INSERT
ON "FORMULAIRE_NATOBS_REPEAT_LOC"
FOR EACH ROW
EXECUTE PROCEDURE proc_natobs();
Hope it work for you.