insert or update in postgresql 9.1 - postgresql

I have a bunch of insert statements with varying number of columns for their respective tables, i need to execute these queries if the record doesn't already exist.I have tried to implement this as
do $$
begin
IF not exists (SELECT 1 FROM gst_type_customer WHERE name = 'Unregistered') THEN
insert into gst_type_customer(create_date,write_date,create_uid,write_uid,name) values((now() at time zone 'UTC'),(now() at time zone 'UTC'),1,1,'Unregistered');
END IF;
end
$$
even though the above code does work although implementing for a bulk of queries would require a lot of time, so i thought of making a stored procedure which i could call as
merge_check(insertquery,name[column to check for duplication],value)
but I'm unable to execute the insert query directly.
so far I have come up with
CREATE OR REPLACE FUNCTION merge_tabla(data text)
RETURNS void AS
$BODY$
BEGIN
execute(data);
END;
$BODY$
LANGUAGE plpgsql
select merge_table("insert into gst_type_customer(name) values('Unregistered')")
but I get an error saying
column "insert into gst_type_customer(name) values('Unregistered')" does not exist

The error You are getting is caused by using double quotes when calling the function. This should work:
select merge_table(E'insert into gst_type_customer(name) values(\'Unregistered\')'::text)
You need to use single quotes (double quotes are used for column names, single quotes for string literals) and escape any single quotes in the original query string.

You can use INSERT ... SELECT like this:
INSERT INTO gst_type_customer(create_date, write_date, create_uid, write_uid, name)
SELECT (now() at time zone 'UTC'), (now() at time zone 'UTC'), 1, 1, 'Unregistered'
WHERE NOT EXISTS (
SELECT *
FROM gst_type_customer
WHERE name = 'Unregistered'
)

postgres 9.1 supports merge command
https://www.postgresql.org/message-id/attachment/23520/sql-merge.html
sample:
MERGE CustomerAccount CA
USING (SELECT CustomerId, Sum(TransactionValue) As TransactionSum
FROM Transactions
WHERE TransactionId > 35345678
GROUP BY CustomerId) AS T
ON T.CustomerId = CA.CustomerId
WHEN MATCHED
UPDATE SET Balance = Balance - TransactionSum
WHEN NOT MATCHED
INSERT (CustomerId, Balance)
VALUES (T.CustomerId, T.TransactionSum)
;

Related

In POSTGRESQL how to get some value from a table into variable of integer type first and then use it latter.?

In POSTGRESQL how to get some value from a table into variable of integer type first and then use it latter.?
Actually I first want to get value in a variable of type integer and then use it in latter select query.
I know all other solution i.e. using join , using ;with clause but I not want all that. I only want what exactly I said.
Any working example will be good. I want all that working in a pgsql
CREATE OR REPLACE FUNCTION public."GetMedLastAdmnstrationDateTime"(_medicationid integer, _alfid integer)
RETURNS TABLE("MedLastAdminDateTime" timestamp without time zone)
LANGUAGE plpgsql
AS $function$
begin
declare _partitiondate timestamp = (select date from table_abc limit 1)
Return Query
select
e."MedDateTime" as "MedLastAdminDateTime"
from public."eMAR" e
where
e."AlfId" = _alfid
and e."MedicationId" = _medicationid
and e."MedDateTime" > _partitiondate::date
order by e."MedDateTime" desc
limit 1
;
END
$function$
;

no function matches the given name and argument types. you might need to add explicit type casts. INSERT on PREPARE

I have this PL/pgSQL function.
CREATE OR REPLACE FUNCTION add_employee_att(emp_id INT, att_time TIMESTAMP)
RETURNS void AS $$
BEGIN
IF NOT EXISTS (SELECT FROM employee_att WHERE employee_id = emp_id AND time_stamp = att_time) THEN
PREPARE prep_att (INT, TIMESTAMP)
AS INSERT INTO employee_att (employee_id, time_stamp) VALUES ($1, $2);
EXECUTE prep_att (emp_id, att_time);
END IF;
END;
$$ LANGUAGE plpgsql;
Then if i execute this:
SELECT add_employee_att(35, '2019-08-29 00:00:25'::timestamp);
I got this error:
Error in query: ERROR: function prep_att(integer, timestamp without time zone) does not exist
LINE 1: SELECT prep_att(emp_id, att_time)
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
QUERY: SELECT prep_att(emp_id, att_time)
CONTEXT: PL/pgSQL function add_employee_attendant(integer,timestamp without time zone) line 6 at EXECUTE
But if replace the PREPARE with this INSERT:
INSERT INTO employee_att (employee_id, time_stamp) VALUES (emp_id, att_time);
It works fine. Any idea what's wrong on the PREPARE stuff ?
Don't use PREPARE in plpgsql. It has not sense. Any embedded SQL is already prepared (execution plan is reused).
So write just
CREATE OR REPLACE FUNCTION add_employee_att(emp_id INT, att_time TIMESTAMP)
RETURNS void AS $$
BEGIN
IF NOT EXISTS (SELECT FROM employee_att
WHERE employee_id = emp_id
AND time_stamp = att_time)
THEN
INSERT INTO employee_att (employee_id, time_stamp) VALUES (emp_id, att_time);
END IF;
END;
$$ LANGUAGE plpgsql;
Attention: this test is example of typical code that is partially useless. It is not protection against duplicates (emp_id, att_time). Only unique index is it. You cannot to know ever, if you see last data. Every time, when you are using SQL, you are working with snapshot of database. But data inside database can be little bit different already.

CREATE TABLE AS is not allowed in a non-volatile function in Postgresql

I have wrote this method on postgresql 10 :
create or replace function get_users()
returns TABLE(user_idd uuid, device_idd text, shapee text , datee timestamp) AS
$$
begin
create temp table lines as
SELECT DISTINCT user_id, device_id from olocations;
select uuid_generate_v4(),
o1.user_id,
o1.device_id,
st_astext(ST_Collect(o1.shape)),
date(o1.creation_date_time) as date
from olocations o1
inner join lines on o1. device_id = lines.device_id and o1.user_id = lines.user_id
where o1.user_id = 'd0edfc59-9923-44c3-9c34-ef5aad3cb810'
and o1.device_id = '89984320001811791540'
group by o1.user_id, o1.device_id, date
order by date ASC ;
DROP TABLE lines;
end
$$
LANGUAGE 'plpgsql'
IMMUTABLE
SECURITY DEFINER
COST 100;
After create method without any problem, when i call my method:
select * from get_users();
I got this error:
sql> select from get_users()
[2018-09-30 17:23:23] [0A000] ERROR: CREATE TABLE AS is not allowed in a non-volatile function
[2018-09-30 17:23:23] Where: SQL statement "create temp table lines as
[2018-09-30 17:23:23] SELECT DISTINCT user_id, device_id from olocations"
[2018-09-30 17:23:23] PL/pgSQL function get_users() line 3 at SQL statement
I think i can not create table in method? right?
The function cannot be IMMUTABLE, define it as VOLATILE.
Per the documentation:
Any function with side-effects must be labeled VOLATILE, so that calls to it cannot be optimized away.
In this case this side-effect is the table creation.
Update.
Use return query to return rows generated by the query:
...
return query
select uuid_generate_v4(),
o1.user_id,
o1.device_id,
st_astext(ST_Collect(o1.shape)),
date(o1.creation_date_time) as date
...

PL/pgSQL: Inserted data not available when function returns

I have an interesting problem that me and my collegue troubles for some time now.
I have a PL/pgSQL function in a PostgreSQL-8.3 (sorry for that old version, I can't change that) that does the following four things:
Get a new serial (ID) from a sequence
Insert a couple of records into a table with that serial
Send a notify signal that an insertion took place
Return the ID to the caller.
Simplified function:
CREATE OR REPLACE FUNCTION add_entry(_user_name text, _visible_attr integer[])
RETURNS bigint AS
$BODY$
DECLARE
user_name text := '#' || $1;
user_id bigint;
BEGIN
-- get the ID from the sequence
SELECT nextval('my_sequence') INTO user_id;
-- insert the name (and some other data not shown here) 5x
FOR item IN 1..5
LOOP
INSERT INTO mytable
(id,index,username,visible)
VALUES (user_id,item,user_name,$2[item]);
END LOOP;
-- send notify that an insertion took place
notify my_notify;
RETURN user_id;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
So, my collegue called this function from his application. He gets the returned ID and uses another thread (DB pooling) in his application to call a function which shall return the data previously inserted with that ID. However, this doesn't work the first time. Only with the second request he is able to select the data. It seems as if that INSERT isn't finished while the function already returns?!
We checked mutiple times, the data will be inserted into the table correctly but somehow it is not available as fast as the return value (the ID from the sequence) is available! Why is that so?
Update: wrong assumption
I examined further and reduced the example to a simple query which really shows the problem:
select * from mytable where id = (select add_entry('MyTestUser'));
This query returns no rows. But if I do that in two seperate steps I can select the data which I inserted with the add_entry function.
I have no clue what I'm doing wrong or how I could speed up the insertion...
From the 8.3 manual
In effect, a SELECT query sees a snapshot of the database as of the instant that that query begins to run
Since the update is done in the select itself the inserted row will not be seen.
http://www.postgresql.org/docs/8.3/static/tutorial-transactions.html
Change the function to return setof mytable. It can be plain SQL. To change the return type the function must be dropped first
drop function add_entry(text);
create or replace function add_entry (_user_name text, _visible_attr integer[])
returns setof mytable as $body$
notify my_notify;
with ins as (
insert into mytable (id, index, username, visible)
select user_id, item, '#' || $1, $2[item]
from
generate_series(1, 5) g(item)
cross join
(values (nextval('my_sequence'))) s(user_id)
returning *
)
select * from ins;
$body$ language sql volatile;
The notification must happen before anything is returned from the function. It is not a problem as if the insert fails the transaction rolls back including the notification. Call it like
select * from add_entry('MyTestUser');
The select will not see the modified table but the returned mytable rows.
If it is necessary for the function to be plpgsql then use return query
create or replace function add_entry (_user_name text, _visible_attr integer[])
returns setof mytable as $body$
begin
notify my_notify;
return query
insert into mytable (id, index, username, visible)
select user_id, item, '#' || $1, $2[item]
from
generate_series(1, 5) g(item)
cross join
(values (nextval('my_sequence'))) s(user_id)
returning *
;
end;
$body$ language plpgsql volatile;

Calculate daily sums in PostgreSQL

I am fairly new in postgres and what am trying to do is calculate sum values for each day for every month (i.e daily sum values). Based on scattering information I came up with something like this:
CREATE OR REPLACE FUNCTION sumvalues() RETURNS double precision AS
$BODY$
BEGIN
FOR i IN 0..31 LOOP
SELECT SUM("Energy")
FROM "public"."EnergyWh" e
WHERE e."DateTime" = day('01-01-2005 00:00:00'+ INTERVAL 'i' DAY);
END LOOP;
END
$BODY$
LANGUAGE plpgsql VOLATILE NOT LEAKPROOF;
ALTER FUNCTION public.sumvalues()
OWNER TO postgres;
The query returned successfully, so I thought I had made it. However when am trying to insert the values of the function to a table (which maybe wrong):
INSERT INTO "SumValues"
("EnergyDC")
(
SELECT sumvalues()
);
I get this:
ERROR: invalid input syntax for type interval: "01-01-2005 00:00:00"
LINE 3: WHERE e."DateTime" = day('01-01-2005 00:00:00'+ INTERVAL...
I tried to debug it myself but yet am not sure, which of the two I am doing wrong (or both) and why.
Here is an example of EnergyWh
(am using systemid and datetime as composite PK, but that should not matter)
see GROUP BY clause http://www.postgresql.org/docs/9.2/static/tutorial-agg.html
SELECT EXTRACT(day FROM e."DateTime"), EXTRACT(month FROM e."DateTime"),
EXTRACT(year FROM e."DateTime"), sum("Energy")
FROM "public"."EnergyWh" e
GROUP BY 1,2,3
but following query should to work too:
SELECT e."DateTime"::date, sum("Energy")
FROM "public"."EnergyWh" e
GROUP BY 1
I am using a short syntax for GROUP BY ~ GROUP BY 1 .. group by first column.
Here is simple Example that can help you:
Table :
create table demo (value double precision);
Function
CREATE OR REPLACE FUNCTION sumvalues() RETURNS void AS
$BODY$
DECLARE
inte text;
BEGIN
FOR i IN 0..31 LOOP
inte := 'INSERT INTO demo SELECT EXTRACT (DAY FROM TIMESTAMP ''01-01-2005 00:00:00''+ INTERVAL '''||i||' Days'')';
EXECUTE inte;
END LOOP;
END
$BODY$
LANGUAGE plpgsql VOLATILE NOT LEAKPROOF;
ALTER FUNCTION public.sumvalues()
OWNER TO postgres;
Function Call
SELECT sumvalues();
Output
SELECT * FROM demo;
Here if you want to use some variable value into SQL query than you must have to use some DYNAMIC QUERY for that.
Reference : Dynamic query in pgsql