Merge Statement with temp table - postgresql

CREATE OR REPLACE FUNCTION public.merge_test (
r_obj refcursor,
_ldeptid character varying
) RETURNS refcursor
LANGUAGE 'plpgsql' COST 100.0 VOLATILE AS $function$
BEGIN
DROP TABLE IF EXISTS tblCumulate;
create temp table tblCumulate (
lCompid varchar(10),
lOpenCount int default 0,
lClosedCount int default 0
);
DROP TABLE IF EXISTS tblOpen;
create temp table tblOpen (
lOSID SERIAL,
lCount numeric(24,0),
lCompid varchar(100)
);
MERGE into tblCumulate CUM using (select lcompid,lCount from tblopen) as OP
on CUM.lcompid=OP.lcompid
when matched
then update set cum.lOpenCount=op.lcount
when not matched
then insert (lCompid,lOpenCount) values op.lcompid,op.lcount);
open r_obj for
select * from tblCumulate;
return r_obj;
END;
$function$;
when I execute (Run) this procedure showing following error.
ERROR: "tblcumulate" is not a known variable
LINE 41: MERGE into tblCumulate CUM temp

There is no MERGE statement in PostgreSQL.
Consider using INSERT ... ON CONFLICT.

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();

Partition on existing table getting an error "infinite recursion detected in rules"

--Existing Data
create table tbl_Master
(
col_Date date not null,
id int not null,
value int not null
);
insert into tbl_Master values('2021-01-14',10,21);
insert into tbl_Master values('2020-09-30',11,22);
insert into tbl_Master values('2021-11-28',12,23);
--alter table name
alter table tbl_Master rename to tbl_Master_old;
--Create Master table again with Partition by range
create table tbl_Master(
col_Date date not null,
id int not null,
value int not null
) partition by range (col_Date);
--Function:
create or replace function fn_create_partition(col_Date date) returns void
as
$body$
declare v_startDate date := date_trunc('month', col_Date)::date;
declare v_EndDate date := (v_startDate + interval '1 month')::date;
declare tableName text := 'tbl_Master_Part_' || to_char(col_Date, 'YYYYmm');
begin
if to_regclass(tableName) is null then
execute format('create table %I partition of tbl_Master for values from (%L) to (%L)', tableName, v_startDate, v_EndDate);
end if;
end;
$body$
language plpgsql;
--Create Partition tables for existing data
do
$$
declare rec record;
begin
for rec in select distinct date_trunc('month', col_Date)::date yearmonth from tbl_Master_old
loop
perform fn_create_partition(rec.yearmonth);
end loop;
end
$$;
--Insert backup data
insert into tbl_Master (col_Date, id, value) select * from tbl_Master_old;
--When I insert record
insert into tbl_Master values('2021-02-22',12,22);
Getting an error:
ERROR: no partition of relation "tbl_master" found for row DETAIL: Partition key of the failing row contains (col_date) = (2021-02-22).
So I have created rule for this:
--Rule
create or replace rule rule_fn_create_partition as on insert
to tbl_Master
do instead
(
select fn_create_partition(NEW.col_Date);
insert into tbl_Master values(New.*)
);
--When I insert record
insert into tbl_Master values('2021-02-22',12,22);
Getting an error:
ERROR: infinite recursion detected in rules for relation "tbl_master"
The rule contains an INSERT, so that is an infinite recursion.
You cannot have PostgreSQL automatically create partitions as you insert data. It just doesn't work. Triggers won't work either. The problem is that that would require modifying the underlying table while the insert is in progress.

column reference "col_1" is ambiguous plpgsql

I'm writing postgresql function which inserts data and returns successfully inserted data rows.
The code is below.
CREATE OR REPLACE FUNCTION public.fn_insert_test(json_data jsonb)
returns table(col_1 varchar(255),
col_2 varchar(255),
col_3 timestamp)
LANGUAGE plpgsql
AS $function$
declare
--
begin
with my_table as (
with my_table1(my_json) as (
values(
json_data
)
)
insert into "test"(col_1, col_2, col_3)
select
elem->>'val1', elem->>'val2', now()
from
my_table1 t, jsonb_array_elements(my_json->'_data') elem
on conflict(col_1) do nothing
returning *
)
select * from my_table;
end
$function$
;
select fn_insert_test('{"_data": [{"val1":"1", "val2":"1"}, {"val1":"2", "val2":"2"}]}');
It occurs error below.
SQL Error [42702]: ERROR: column reference "col_1" is ambiguous
Detail: It could refer to either a PL/pgSQL variable or a table column.
Where: PL/pgSQL function fn_insert_test(jsonb) line 5 at SQL statement
[edit]
This is not the real problem here. There is a lot of problems in your code :
-> If you want to return a table, you have to use 'return query' : http://www.postgresqltutorial.com/plpgsql-function-returns-a-table/
-> I do not think you can use the WITH like you do: https://www.postgresql.org/docs/current/queries-with.html
Here is a functoinnal version of your code. But I am not quite sure of what you want :
CREATE OR REPLACE FUNCTION public.fn_insert_test(json_data jsonb)
returns table(col_1 varchar(255),
col_2 varchar(255),
col_3 timestamp)
LANGUAGE plpgsql AS
$$
declare
--json_data jsonb := '{"_data": [{"val1":"1", "val2":"1"}, {"val1":"2", "val2":"2"}]}';
begin
create temp table res (col_1 varchar (255), col_2 varchar (255), col_3 timestamp) on commit drop;
with my_table1(my_json) as
(
values(
json_data
)
)
, inserted as
(
insert into test(col_1, col_2, col_3)
select
elem->>'val1', elem->>'val2', now()
from
my_table1 t
, jsonb_array_elements(json_data->'_data') elem
on conflict do nothing
returning *
)
insert into res
select *
from inserted;
--raise notice '%', res_v;
return query select * from res;
end
$$
;
select fn_insert_test('{"_data": [{"val1":"1", "val2":"1"}, {"val1":"2", "val2":"2"}]}');

Postgres create universal function on all table

I have a few db tables.
I want write universtal postgres function on copy rows to history tables
I have tables:
table1
table1_h
table2
table2_h
I wrote function (with help stackoverflow)
CREATE OR REPLACE FUNCTION copy_history_f() RETURNS TRIGGER AS
$BODY$
DECLARE
tablename_h text:= TG_TABLE_NAME || '_h';
BEGIN
EXECUTE 'INSERT INTO ' || quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(tablename_h) || ' VALUES (' || OLD.* ||')';
RETURN NULL;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
And functions was create, but after update is error.
ERROR: syntax error at or near ","
ROW 1: ...RT INTO table1_h VALUES ((12,,,0,,"Anto...
I know where is error in this insert but I don't know how I repair that.
Structure tables table1 and table1_h are identical but table1_h has one more column (id_h)
Can you help me, how I have create psql function?
Thnak you.
drop table if exists t;
drop table if exists t_h;
drop function if exists ftg();
create table t(i serial, x numeric);
insert into t(x) values(1.1),(2.2);
create table t_h(i int, x numeric);
create function ftg() returns trigger language plpgsql as $ftg$
declare
tablename_h text:= TG_TABLE_NAME || '_h';
begin
execute format($q$ insert into %I.%I select $1.*; $q$, TG_TABLE_SCHEMA, tablename_h) using old;
return null;
end $ftg$;
create trigger tg_t after delete on t for each row execute procedure ftg();
delete from t where i = 1;
select * from t_h;
dbfiddle
Update It solves your problem, but I think that you want to have a bit more info in your history tables. It will be more complex a bit:
drop table if exists t;
drop table if exists t_h;
drop function if exists ftg();
create table t(i serial, x numeric);
insert into t(x) values(1.1),(2.2);
create table t_h(
hi serial, -- just ID
hd timestamp, -- timestamp
hu text, -- user who made changes
ha text, -- action
i int, x numeric
);
create function ftg() returns trigger language plpgsql as $ftg$
declare
tablename_h text:= TG_TABLE_NAME || '_h';
begin
execute format(
$q$
insert into %I.%I
select
nextval(%L || '_hi_seq'),
clock_timestamp(),
current_user,
%L,
$1.*
$q$, TG_TABLE_SCHEMA, tablename_h, tablename_h, TG_OP) using old;
return null;
end $ftg$;
create trigger tg_t after delete or update on t for each row execute procedure ftg();
update t set x = x * 2;
update t set x = x * 2 where i = 2;
delete from t where i = 1;
select * from t_h;
dbfiddle
I assume you are inserting the 'old' values from table1 into table1_h.
The additional column is your problem. When you using an insert without naming columns you must use a matching number and type for the insert.
You must use column referencing.
eg.
Insert into table1_h(column1, column2, column3)
values (a,b,c)
Consider a default value for the additional column in table table1_h.

Trigger insert into another table only if unique value

I have a trigger function that copy row of unique values to another table on update or insert that ALMOST work.
The trigger should only insert a new row to the sample table if the number don't exist in it before. Atm. it insert a new row to the sample table with the value NULL if the number already exist in the table. I dont want it to do anything if maintbl.number = sample.nb_main
EDIT: sample table and sample data
CREATE TABLE schema.main(
sid SERIAL NOT NULL,
number INTEGER,
CONSTRAINT sid_pk PRIMARY KEY (sid)
)
CREATE TABLE schema.sample(
gid SERIAL NOT NULL,
nb_main INTEGER,
CONSTRAINT gid_pk PRIMARY KEY (gid)
Example and desired result
schema.main schema.sample
number nb_main
234233 234233
234234 555555
234234
555555
555555
CREATE OR REPLACE FUNCTION schema.update_number()
RETURNS trigger AS
$BODY$
BEGIN
INSERT INTO schema.sample(
nb_main)
SELECT DISTINCT(maintbl.number)
FROM schema.maintbl
WHERE NOT EXISTS (
SELECT nb_main FROM schema.sample WHERE maintbl.number = sample.nb_main);
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION schema.update_number()
OWNER TO postgres;
CREATE TRIGGER update_number
AFTER INSERT OR UPDATE
ON schema.maintbl
FOR EACH ROW
EXECUTE PROCEDURE schema.update_number();
I just found out that my select query is probably wrong, if I run SELECT query by itself it return one row 'NULL' but i should not?
SELECT DISTINCT(maintbl.number)
FROM schema.maintbl
WHERE NOT EXISTS (
SELECT nb_main FROM schema.sample WHERE maintbl.number = sample.nb_main);
Any good advice?
Best
If I understood correctly, you wish to append to schema.sample a number that has been inserted or updated in schema.maintbl, right?
CREATE OR REPLACE FUNCTION schema.update_number()
RETURNS trigger AS
$BODY$
BEGIN
IF (SELECT COUNT(*) FROM schema.sample WHERE number = NEW.number) = 0 THEN
INSERT INTO schema.sample(nb_main) VALUES (NEW.number);
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;