pass parameters in crosstab query postgres - postgresql

How to pass a parameter in crosstab query in postgresql.
Please refer the below function in postgresql
create function sp_nextfollowup_count_byweek(_from timestamp without time zone,_to timestamp without time zone)
returns table(userid integer,username character varying,day1 bigint,day2 bigint,day3 bigint,day4 bigint,day5 bigint,day6 bigint,day7 bigint)as
$BODY$
BEGIN
return query
with cte as(
select * from crosstab($$
select follow_up_by,next_follow_up_date,count(*) from sales_enquiry_follow_up where next_follow_up_date between _from and _to
group by follow_up_by,next_follow_up_date
order by follow_up_by,next_follow_up_date$$,$$select date::timestamp without time zone
from generate_series(
_from,
_to,
'1 day'::interval
) date$$)
as(id integer, dd bigint,dd1 bigint,dd2 bigint,dd3 bigint,dd4 bigint,dd5 bigint,dd6 bigint)
)
select cte.id,u.username,cte.dd,cte.dd1,cte.dd2,cte.dd3,cte.dd4,cte.dd5,cte.dd6 from cte left join users u on cte.id=u.id;
END;
$BODY$
language plpgsql;
I am getting this error column "_from" does not exist. How to solve this issue?

Because argument of crosstab is just string you must include arguments directly:
$$
select follow_up_by,next_follow_up_date,count(*) from sales_enquiry_follow_up where next_follow_up_date between $$ || quote_literal(_from) || $$ and $$ || quote_literal(_to) || $$
group by follow_up_by,next_follow_up_date
order by follow_up_by,next_follow_up_date$$

Related

PostgreSQL Function returning only one row

I am writing a Function to accept a list by a parameter and return some set of records. When I run the select query alone, it is showing all the rows. But from Function I'm getting only the top row. Still searching about an hour, didn't get solutions.
Here is the Function query
CREATE OR REPLACE FUNCTION get_transaction_all_property(p_property character varying, p_year timestamp without time zone)
RETURNS SETOF record
LANGUAGE plpgsql
AS $function$
DECLARE
l_num_property numeric(15,2);
l_num_strtamt numeric(15,2);
l_num_endamt numeric(15,2);
l_property varchar := '';
l_year timestamp := LOCALTIMESTAMP;
result RECORD;
BEGIN
IF ( p_property IS NOT NULL ) THEN
l_property := p_property;
END IF;
IF ( p_year IS NOT NULL ) THEN
l_year := p_year;
END IF;
SELECT INTO l_num_property, l_num_strtamt, l_num_endamt
property, coalesce(sum(strtamt),0)::numeric(15,2), coalesce(sum(endamt),0)::numeric(15,2) from (
(select a.property as property, SUM(b.strtamtg + b.strtamtl) AS strtamt, SUM(b.endamtg + b.endamtl) AS endamt
FROM "myTransactions" AS a
WHERE a.property::text = ANY(STRING_TO_ARRAY(l_property,',')) AND a.period < l_year
group by a.property)
)as doo group by property;
SELECT INTO result l_num_property, l_num_strtamt, l_num_endamt;
RETURN next result;
END;
$function$
;
-- Permissions
ALTER FUNCTION get_transaction_all_property(varchar,timestamp,int8) OWNER TO mysuer;
GRANT ALL ON FUNCTION get_transaction_all_property(varchar,timestamp,int8) TO mysuer;
Here is the Function Call from SSRS:
select * from get_transaction_fund_totals_year_recon_sf_new(?,?) as ("property" numeric, "initial" numeric, "end" numeric)
SSRS Parameter Expression:
=Join(Parameters!pty.Value,",")
=Join(Parameters!dat.Value,",")
Please any one guide me to do this.
Thanks in Advance
The PL/pgSQL construct SELECT ... INTO will silently discard all but the first result rows.
Instead of doing this:
SELECT INTO l_num_property, l_num_strtamt, l_num_endamt ...;
SELECT INTO result l_num_property, l_num_strtamt, l_num_endamt;
RETURN next result;
do this:
RETURN QUERY SELECT ...;

How to do postgresql select query funciton using parameter?

I want to create a postgresql funciton that returns records. But if I pass an id parameter, it should be add in where clause. if I do not pass or null id parameter, where clasuse will not add the query.
CREATE OR REPLACE FUNCTION my_func(id integer)
RETURNS TABLE (type varchar, total bigint) AS $$
DECLARE where_clause VARCHAR(200);
BEGIN
IF id IS NOT NULL THEN
where_clause = ' group_id= ' || id;
END IF ;
RETURN QUERY SELECT
type,
count(*) AS total
FROM
table1
WHERE
where_clause ???
GROUP BY
type
ORDER BY
type;
END
$$
LANGUAGE plpgsql;
You can either use one condition that takes care of both situations (then you don't need PL/pgSQL to begin with):
CREATE OR REPLACE FUNCTION my_func(p_id integer)
RETURNS TABLE (type varchar, total bigint)
AS $$
SELECT type,
count(*) AS total
FROM table1
WHERE p_id is null or group_id = p_id
GROUP BY type
ORDER BY type;
$$
LANGUAGE sql;
But an OR condition like that is typically not really good for performance. The second option you have, is to simply run two different statements:
CREATE OR REPLACE FUNCTION my_func(p_id integer)
RETURNS TABLE (type varchar, total bigint)
AS $$
begin
if (p_id is null) then
return query
SELECT type,
count(*) AS total
FROM table1
GROUP BY type
ORDER BY type;
else
return query
SELECT type,
count(*) AS total
FROM table1
WHERE group_id = p_id
GROUP BY type
ORDER BY type;
end if;
END
$$
LANGUAGE plgpsql;
And finally you can build a dynamic SQL string depending the parameter:
CREATE OR REPLACE FUNCTION my_func(p_id integer)
RETURNS TABLE (type varchar, total bigint)
AS $$
declare
l_sql text;
begin
l_sql := 'SELECT type, count(*) AS total FROM table1 '
if (p_id is not null) then
l_sql := l_sql || ' WHERE group_id = '||p_id;
end if;
l_sql := l_sql || ' GROUP BY type ORDER BY type';
return query execute l_sql;
end;
$$
LANGUAGE plpgsql;
Nothing is required just to use the variable as it is for more info please refer :plpgsql function parameters

Using parameters passed to stored procedure in query

I have a sql query where I want to extract records older than 'X' number of days, here for eg its 7 days:
SELECT * FROM BOOKMARK.MONITORING_TABLE WHERE inserteddatetime < (now() - '7 day'::interval);
I have to execute this query through a stored procedure passing in the configurable 'X' no of days as arguments.
The procedure is as below:
CREATE OR REPLACE FUNCTION DELETE_REDUNDANT_RECORDS_STORED_PROCEDURE(days int)
RETURNS void AS
$func$
DECLARE
rec_old RECORD;
cursor_data CURSOR FOR
SELECT * FROM BOOKMARK.MONITORING_TABLE WHERE inserteddatetime < now() - '$1 day'::interval;
BEGIN
OPEN cursor_data;
// business logic for the procedure
CLOSE cursor_data;
END;
$func$
LANGUAGE plpgsql;
This doesn't work as I am not able to use the placeholder for days in my query. How do we use the arguments passed to my query in this case.
To create an interval based on an integer, make_interval() is much easier to use than casting to an interval type.
Additional I wouldn't use a cursor, but a FOR loop based on a SELECT statement (maybe using make_interval(days => $1) works in the cursor declaration as well)
CREATE OR REPLACE FUNCTION DELETE_REDUNDANT_RECORDS_STORED_PROCEDURE(days int)
RETURNS void AS
$func$
DECLARE
rec_old record;
BEGIN
for rec_old in SELECT *
FROM BOOKMARK.MONITORING_TABLE
WHERE inserteddatetime < now() - make_interval(days => $1)
loop
raise notice 'records %', rec_old;
end loop;
END;
$func$
LANGUAGE plpgsql;

How to use argument for table name in dynamic SQL

I am writing a Postgres function to get the number of new records in a table. Here table name is a variable.
create or replace function dmt_mas_updates(
tb_name text,
days integer)
returns integer as
$$
declare
ct integer;
begin
execute 'select count(*) from $1 where etl_create_dtm > now() - $2 * interval ''1 days'' '
using tb_name, days into ct;
return ct;
end;
$$ LANGUAGE 'plpgsql'
When I call the function with select * from dmt_mas_updates('dmt_mas_equip_store_dim',2);, I got syntax error at $1.
If I run the query directly select count(*) from dmt_mas_equip_store_dim where etl_create_dtm >= interval '3 days', it works correctly.
Why am I getting this error? What did I do wrong?
Per the documentation:
Note that parameter symbols can only be used for data values — if you want to use dynamically determined table or column names, you must insert them into the command string textually.
Use the format() function:
create or replace function dmt_mas_updates(
tb_name text,
days integer)
returns integer as
$$
declare
ct integer;
begin
execute format(
'select count(*) from %I where etl_create_dtm > now() - $1 * interval ''1 days'' ',
tb_name)
using days into ct;
return ct;
end;
$$ LANGUAGE 'plpgsql';

PostgreSQL 9.3: Check only time from timestamp

I have the following table with one field of type timestamp.
Create table Test_Timestamp
(
ColumnA timestamp
);
Now inserting some records for demonstration:
INSERT INTO Test_Timestamp VALUES('1900-01-01 01:21:15'),
('1900-01-01 02:11:25'),
('1900-01-01 12:52:10'),
('1900-01-01 03:20:05');
Now I have created function Function_Test with two parameters namely St_time and En_Time which
are of type varchar, In which I only pass the time like 00:00:01. And after that Function has
to return the table with that condition of two time's parameters.
CREATE OR REPLACE FUNCTION Function_Test
(
St_Time varchar,
En_Time varchar
)
RETURNS TABLE
(
columX timestamp
)
AS
$BODY$
Declare
sql varchar;
wher varchar;
BEGIN
wher := 'Where columna BETWEEN '|| to_char(cast(St_Time as time),'''HH24:MI:SS''') ||' AND '|| to_char(cast(En_Time as time),'''HH24:MI:SS''') ||'';
RAISE INFO '%',wher;
sql := 'SELECT * FROM Test_Timestamp ' || wher ;
RAISE INFO '%',sql;
RETURN QUERY EXECUTE sql;
END;
$BODY$
LANGUAGE PLPGSQL;
---Calling function
SELECT * FROM Function_Test('00:00:00','23:59:59');
But getting an error:
ERROR: invalid input syntax for type timestamp: "00:00:01"
LINE 1: ...ELECT * FROM Test_Timestamp where ColumnA BETWEEN '00:00:01'...
You can cast the column to a time: ColumnA::time
You should also not pass a time (or a date, or a timestamp) as a varchar. And you don't need dynamic SQL or a PL/pgSQL function for this:
CREATE OR REPLACE FUNCTION Function_Test(St_Time time, en_Time time)
RETURNS TABLE (columX timestamp)
AS
$BODY$
SELECT *
FROM Test_Timestamp
where columna::time between st_time and en_time;
$BODY$
LANGUAGE sql;
Call it like this:
select *
from Function_Test(time '03:00:00', time '21:10:42');
You can use extract to the hour, minute and second
http://www.postgresql.org/docs/9.3/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT