How to implement stored procedure/function in postgresql version 12? - postgresql

How I can implement it? I am confused in syntax also?
CREATE OR REPLACE Function dailyyyy_volation_routine(_ispeed_count integer,_rlvd_count integer,_stopline_count integer,_wrongdir_count integer
,_wronglane_count integer,_zebracross_count integer,_total_count integer)
RETURNS void AS $$
BEGIN
INSERT INTO temp_daily_stats(ispeed_count,rlvd_count,stopline_count,wrongdir_count,wronglane_count,zebracross_count,total_count)
with t1 as (SELECT SUM(ispeed_count) as ispeed_count from temp_hourly_stats),
t3 as (SELECT SUM(rlvd_count) as rlvd_count from temp_hourly_stats),
t4 as (SELECT SUM(stop_line_count) as stopline_count from temp_hourly_stats),
t5 as (SELECT SUM(wrong_dir_count) as wrong_dir_count from temp_hourly_stats),
t6 as (SELECT SUM(wrong_lane_count) as wrong_lane_count from temp_hourly_stats),
t7 as (SELECT SUM(zebra_cross_count) as zebra_cross_count from temp_hourly_stats),
t8 as (SELECT #tc := SUM(total_count) as total_day_count from temp_hourly_stats),
SELECT DISTINCT t1.ispeed_count,t3.rlvd_count,t4.stopline_count,t5.wrong_dir_count,t6.wrong_lane_count,t7.zebra_cross_count,t8.total_day_count
FROM t1,t3,t4,t5,t6,t7,t8 limit 1;
END
$$
LANGUAGE SQL;
I want to put data from one table to another using postgresql function?*

Here is an attempt to translate your function to correct code:
CREATE OR REPLACE Function dailyyyy_volation_routine(
_ispeed_count integer,
_rlvd_count integer,
_stopline_count integer,
_wrongdir_count integer,
_wronglane_count integer,
_zebracross_count integer,
_total_count integer) RETURNS void AS
$$INSERT INTO temp_daily_stats(
ispeed_count,
rlvd_count,
stopline_count,
wrongdir_count,
wronglane_count,
zebracross_count,
total_count
)
SELECT SUM(ispeed_count),
SUM(rlvd_count),
SUM(stop_line_count),
SUM(wrong_dir_count),
SUM(wrong_lane_count),
SUM(zebra_cross_count),
SUM(total_count)
FROM temp_hourly_stats$$
LANGUAGE SQL;

Related

PgSQL function returning table and extra data computed in process

In PgSQL I make huge select, and then I want count it's size and apply some extra filters.
execute it twice sound dumm,
so I wrapped it in function
and then "cache" it and return union of filtered table and extra row at the end where in "id" column store size
with q as (select * from myFunc())
select * from q
where q.distance < 400
union all
select count(*) as id, null,null,null
from q
but it also doesn't look like proper solution...
and so the question: is in pg something like "generator function" or any other stuff that can properly solve this ?
postgreSQL 13
myFunc aka "selectItemsByRootTag"
CREATE OR REPLACE FUNCTION selectItemsByRootTag(
in tag_name VARCHAR(50)
)
RETURNS table(
id BIGINT,
name VARCHAR(50),
description TEXT,
/*info JSON,*/
distance INTEGER
)
AS $$
BEGIN
RETURN QUERY(
WITH RECURSIVE prod AS (
SELECT
tags.name, tags.id, tags.parent_tags
FROM
tags
WHERE tags.name = (tags_name)
UNION
SELECT c.name, c.id , c.parent_tags
FROM
tags as c
INNER JOIN prod as p
ON c.parent_tags = p.id
)
SELECT
points.id,
points.name,
points.description,
/*points.info,*/
points.distance
from points
left join tags on points.tag_id = tags.id
where tags.name in (select prod.name from prod)
);
END;
$$ LANGUAGE plpgsql;
as a result i want see maybe set of 2 table or generator function that yield some intermediate result not shure how exacltly it should look
demo
CREATE OR REPLACE FUNCTION pg_temp.selectitemsbyroottag(tag_name text, _distance numeric)
RETURNS TABLE(id bigint, name text, description text, distance numeric, count bigint)
LANGUAGE plpgsql
AS $function$
DECLARE _sql text;
BEGIN
_sql := $p1$WITH RECURSIVE prod AS (
SELECT
tags.name, tags.id, tags.parent_tags
FROM
tags
WHERE tags.name ilike '%$p1$ || tag_name || $p2$%'
UNION
SELECT c.name, c.id , c.parent_tags
FROM
tags as c
INNER JOIN prod as p
ON c.parent_tags = p.id
)
SELECT
points.id,
points.name,
points.description,
points.distance,
count(*) over ()
from points
left join tags on points.tag_id = tags.id
where tags.name in (select prod.name from prod)
and points.distance > $p2$ || _distance
;
raise notice '_sql: %', _sql;
return query execute _sql;
END;
$function$
You can call it throug following way
select * from pg_temp.selectItemsByRootTag('test',20);
select * from pg_temp.selectItemsByRootTag('test_8',20) with ORDINALITY;
The 1 way to call the function, will have a row of total count total number of rows. Second way call have number of rows plus a serial incremental number.
I also make where q.distance < 400 into function input argument.
selectItemsByRootTag('test',20); means that q.distance > 20 and tags.name ilike '%test%'.

CREATE TABLE is not allowed in a non-volatile function

I have the following three tables :
create table drugs(
id integer,
name varchar(20),
primary key(id)
);
create table prescription(
id integer,
drug_id integer,
primary key(id),
foreign key(drug_id) references drugs(id)
);
create table visits(
patient_id varchar(10),
prescription_id integer,
primary key( patient_id , prescription_id),
foreign key(prescription_id) references prescription(id)
);
I wrote the following function on these tables to show me a patient's drugs list(the patient id is parameter):
CREATE OR REPLACE FUNCTION public.patients_drugs(
patientid character varying)
RETURNS TABLE(drug_id integer, drug_name character varying)
LANGUAGE 'plpgsql'
COST 100
STABLE STRICT
ROWS 1000
AS $BODY$
begin
create temporary table result_table(
drug_id integer,
drug_name varchar(20)
);
return query select distinct drug.id , drug.name
from visits join prescription
on visits.patient_id = patientID;
end;
$BODY$;
However, it gives me this error:
CREATE TABLE is not allowed in a non-volatile function
You don't need to create a table in order to be able to "return a table". Just get rid of the CREATE TABLE statement.
But your query isn't correct either, as you are selecting columns from the drug table, but you never include that in the FROM clause. You can also get rid of the distinct clause if you don't use a join, but an EXISTS condition:
CREATE OR REPLACE FUNCTION public.patients_drugs(p_patientid character varying)
RETURNS TABLE(drug_id integer, drug_name character varying)
LANGUAGE plpgsql
AS $BODY$
begin
return query
select d.*
from drugs d
where exists (select *
from prescription p
join visits v on v.prescription_id = p.id
where d.id = p.drug_id
and v.patientid = p_patientid);
end;
$BODY$;
Or better, use a simple SQL function:
CREATE OR REPLACE FUNCTION public.patients_drugs(p_patientid character varying)
RETURNS TABLE(drug_id integer, drug_name character varying)
LANGUAGE sql
AS
$BODY$
select d.*
from drugs d
where exists (select *
from prescription p
join visits v on v.prescription_id = p.id
where d.id = p.drug_id
and v.patientid = p_patientid);
$BODY$;

Turning a pl/pgSQL select statement into an insert

I'm trying to turn this select query into an insert query to insert the results into another table, but PostgreSQL is telling me no. This query works and returns a 'reports' data type:
drop function counties();
create or replace function counties()
returns table(code varchar(16), county varchar(50), count bigint) as $$
DECLARE
new_version_number varchar(50) := concat('gcversa00', MAX("versionnumber")) from "gcdefault"."versionhistory";
old_version_number varchar(50) := concat('gcversa00', MAX("versionnumber"-2)) from "gcdefault"."versionhistory";
BEGIN RETURN QUERY EXECUTE
format(
'with cte_current as (select distinct a.code as code, b.countyname as county from %I.servicearea a, public.counties b
where st_intersects(a.geom,b.geom) = True group by a.code, b.countyname),
cte_new as (select distinct a.code as code, b.countyname as county from %I.servicearea a, public.counties b
where st_intersects(a.geom,b.geom) = True group by a.code, b.countyname),
cte_union as (select code, county from cte_current
union all
select code, county from cte_new)
select code,county, count(*) as count
from cte_union
group by code, county
Having count (*) <> 2', new_version_number, old_version_number
);
END;
$$
LANGUAGE plpgsql;
When I turn this into an insert query and call select counties():
drop function counties();
create or replace function counties()
returns table(code varchar(16), county varchar(50), count bigint) as $$
DECLARE
new_version_number varchar(50) := concat('gcversa00', MAX("versionnumber")) from "gcdefault"."versionhistory";
old_version_number varchar(50) := concat('gcversa00', MAX("versionnumber"-2)) from "gcdefault"."versionhistory";
BEGIN RETURN QUERY EXECUTE
format(
'
with cte_current as (select distinct a.code as code, b.countyname as county from %I.servicearea a, public.counties b
where st_intersects(a.geom,b.geom) = True group by a.code, b.countyname),
cte_new as (select distinct a.code as code, b.countyname as county from %I.servicearea a, public.counties b
where st_intersects(a.geom,b.geom) = True group by a.code, b.countyname),
cte_union as (select code, county from cte_current
union all
select code, county from cte_new)
insert into county_check (code, county, count)
select code, county, count(*) as count from cte_union
group by code, county
Having count (*) <> 2', new_version_number, old_version_number
);
END;
$$
LANGUAGE plpgsql;
This is the error I get:
ERROR: cannot open INSERT query as cursor
CONTEXT: PL/pgSQL function counties() line 5 at RETURN QUERY
How can I make this work as an insert statement similar to the way I have it laid out now (if possible)? I looked at creating some temp tables instead of using the cte's in the query, and then using a select into loop, but I can't find any examples this complex that I can use.
you can add RETURNING to fix it (to actually return declared return type), eg:
t=# create table so(i int);
CREATE TABLE
t=# create or replace function f() returns table (i int) as
$$
begin
return query execute format ('
with c(c) as (select 2)
, i as (insert into so select c from c returning *)
select * from i');
end;
$$
language plpgsql;
CREATE FUNCTION
t=# select f();
f
---
2
t=# select * from so;
i
---
2
(1 row)
but in your case why not just:
insert into county_check (code, county, count) select * from counties()

postgresql column names as variable in a subquery

table1
id col1 col2 col3...
table2
col_id col_name
3432 col1
5342 col2
6756 col3
Now I want to generate table 3 like this:
id col_name col_value col_id
Please note that col1, col2,col3... are not in order. Therefore I have to query table2 to obtain col_id ( I think pivot does not work here)
How can I do it in SQL?
It appears that you want a select like this:
SELECT t2.id,
CASE
WHEN t2.col_name='col1' THEN t1.col1
WHEN t2.col_name='col2' THEN t1.col2
WHEN t2.col_name='col2' THEN t1.col2
-- ... more columns
ELSE NULL
END
FROM table2 t2 LEFT JOIN table2 t1 ON t2.col_id = t1.id
You could also create a function, though this will be slower in practice:
CREATE OR REPLACE FUNCTION table1_col(id integer, name text) RETURNS text as $$
DECLARE
col_val text;
BEGIN
EXECUTE format('SELECT %s FROM table1 WHERE id=$1', name)
INTO col_val
USING id;
RETURN col_val;
END;
$$ LANGUAGE plpgsql;
SELECT table1_col(col_id,col_name) FROM table2;

Postgres Stored Procedure issue - how do I solve this?

I am creating a stored procedure that looks like this:
CREATE FUNCTION get_components(_given_user_id integer) RETURNS TABLE (id integer, name varchar, active boolean, parent integer, type smallint, description varchar, email varchar, user_id integer, component_id integer, flag smallint) AS $body$
BEGIN
RETURN QUERY SELECT s.* FROM st_components s LEFT JOIN (SELECT a.* FROM st_users_components_perms a WHERE a.user_id=_given_user_id) ON a.component_id=s.id ORDER BY name;
END;
$body$ LANGUAGE plpgsql;
The problem is, ON a.component_id=s.id ORDER BY name doesn't work because a.component_id is out of scope at this point. Is there a way to declare "a" as st_users_components_perms outside of the query? How is this solved? Thank you very much for any insight!
SELECT
s.*
FROM
st_components s
LEFT JOIN
(SELECT
a.*
FROM
st_users_components_perms a
WHERE
a.user_id = <CONSTANT>
) AS x
ON
x.component_id = s.id
ORDER BY
name;
Though the query seems pointless but let's assume it's a simplified version of your real query.