can not pass date to function correctly - postgresql

CREATE or replace FUNCTION testing_lockstep(_id_arr int[], _counter_arr int[], d_date date)
RETURNS TABLE (uc__id int)
LANGUAGE plpgsql AS
$func$
DECLARE
_id int;
_counter int;
d_date date;
BEGIN
FOR _id, _counter IN
SELECT *
FROM unnest (_id_arr, _counter_arr) t -- !!
LOOP
RETURN QUERY
with orig_dataset as (
select a.uc_id, cr.imei,cr.points_geom ,cr.created_at as time_created
from campaign_routes cr
left join assets a on a.imei = cr.imei
where cr.created_at::date > d_date
)
select uc_id
from orig_dataset;
END LOOP;
END
$func$;
My function call is as follows:
SELECT * FROM testing_lockstep('{454,454}'::int[]
, '{2,3}'::int[], TO_DATE('2017-01-03','YYYY-MM-DD') );
The issue is I am getting a null result set, even though if I hardcode the same date in the function body, then I am getting correct results, i.e. if I write:
where cr.created_at::date > '2023-01-17'::date
then I am getting correct results.
Please note, in the main function body, I have ommited parts for the sake of brevity.

Apparently it is allowed to declare a variable with the same name as a parameter, that is than used instead of the parameter.
Simple remove the duplicated declaration of d_date from your function and it will start to work;)
CREATE or replace FUNCTION testing_lockstep(_id_arr int[], _counter_arr int[], d_date date)
RETURNS TABLE (uc__id int)
LANGUAGE plpgsql AS
$func$
DECLARE
_id int;
_counter int;
-- comment this out -->>>> d_date date;

Related

timestampz column not queryable in function

I have a function call like :
SELECT * FROM f_loop_in_lockstep_final('{454,454}'::int[]
, '{2,3}'::int[], to_date('2023-01-17','YYYY-MM-DD'));
I have declared funtion as :
CREATE or replace FUNCTION f_loop_in_lockstep_final(_id_arr int[], _counter_arr int[], d_date date)
In the function body I have written the following where clause (simplified):
select * from routes where
routes.time_created between ((d_date::date + '11:59:00'::time) at time zone '+05:00') and ((d_date::date + '18:00:00'::time) at time zone '+05:00')
This is giving a null result set (although no errors are displayed), whereas I know for sure that it should be non null result set.
P.S - Here is the simplified code of my function:
CREATE or replace FUNCTION f_loop_in_lockstep_final(_id_arr int[], _counter_arr int[], d_date date)
RETURNS TABLE (uc_name_ varchar)
LANGUAGE plpgsql AS
$func$
DECLARE
_id int;
_counter int;
d_date date;
BEGIN
FOR _id, _counter IN
SELECT *
FROM unnest (_id_arr, _counter_arr) t
LOOP
RETURN QUERY
with orig_dataset as (
select routes
from campaign_routes cr
where cr.created_at between ((d_date::date + '11:59:00'::time) at time zone '+05:00') and ((d_date::date + '18:00:00'::time) at time zone '+05:00')
)
-- a couple of further CTE's result in a final CTE called final_cte
select * from final_cte;
END LOOP;
END
$func$;

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 ...;

passing a list of varchars into plpgsql function

I would like to prepare a function which would be called
select gettvmlistic3('IC','AUTABB',array['565,568,569,570,572,573,574,575,576,577,578'])
the declaratin of my function is
create or replace function getTVMListIC3(operatorName varchar,groupid varchar, ids varchar[])
returns varchar as $$
declare
tvms varchar[];
res varchar;
begin
EXECUTE format('SELECT ARRAY (SELECT id from "%s".dictionary where groupid = ''%s'' and id = ANY(''%s'') order by id)', operatorName, groupid, ids) INTO tvms;
SELECT ARRAY_TO_STRING(tvms, ',') INTO res;
return res;
end;
$$
language plpgsql;
but the result I get is empty. In fact I should receive the same number of elements as in the array provided. What am I doing wrong?
You are passing an array with a single element.
If you want to pass multiple elements you need to use:
array['565','568','569','570','572','573','574','575','576','577','578']
But you shouldn't concatenate the parameter values like that.
Use placeholders ($1) in the dynamic SQL and pass the values with the USING clause. Identifiers should be injected with the %I in the format() function:
You also don't need to first select into an array and then convert that to a string again. You can aggregate everything into a single string right away.
create or replace function getTVMListIC3(operatorName varchar, groupid varchar, ids varchar[])
returns varchar
as $$
declare
res varchar;
begin
EXECUTE format($sql$
SELECT string_agg(id::text, ',' order by id)
from %I.dictionary
where groupid = $1
and id = ANY($2)
$sql$, operatorName)
INTO res
using groupid, ids;
return res;
end;
$$
language plpgsql
Those strings look like numbers. If that is the case it's better to define the parameter as int[] and then pass the list of number as:
array[565,568,569,570,572,573,574,575,576,577,578]

postgres how get get multiple columns values?

CREATE OR REPLACE FUNCTION "public"."sxfun"("jcbh" text)
RETURNS "pg_catalog"."int4" AS $BODY$
declare leftplayer TEXT;
declare rightplayer TEXT;
declare leftcoin int;
BEGIN
SELECT player1 into leftplayer,player2 into rightplayer FROM table1 WHERE id=$1;
SELECT SUM(playcoin) into leftcoin FROM table2 WHERE playname=leftplayer
COMMIT;
END$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
this code syntax error,let how to solve it,please
You are missing a return statement. In PL/pgSQL, declare starts a block, there is no need to repeat the keyword for every variable. And you can't commit in a function - and you don't need it to begin with.
As documented in the manual you need to use return to return a value from a function.
To store multiple columns into multiple variables, you need to separate them with a comma, not repeat the INTO clause.
Note that sum() returns a bigint, so your variable and return type should also be defined as bigint.
CREATE OR REPLACE FUNCTION public.sxfun(jcbh text)
RETURNS bigint
AS
$BODY$
declare
leftplayer TEXT;
rightplayer TEXT;
leftcoin bigint;
BEGIN
SELECT player1, player2
into leftplayer, rightplayer
FROM table1
WHERE id = jcbh;
SELECT SUM(playcoin)
into leftcoin
FROM table2
WHERE playname = leftplayer;
return leftcoin; --<< return the value
END
$BODY$
LANGUAGE plpgsql;
If id is a number (which the name usually indicates), the parameter jcbh should be declared as integer, not as text.
Note that you can simplify this to a single statement. There is no need for intermediate variables:
CREATE OR REPLACE FUNCTION public.sxfun(jcbh text)
RETURNS bigint
AS
$BODY$
SELECT SUM(playcoin)
FROM table2
WHERE playname IN (select leftplayer
FROM table1
WHERE id = jcbh);
$BODY$
LANGUAGE sql;

Merge multiple result tables and perform final query on result

I have a function returning table, which accumulates output of multiple calls to another function returning table. I would like to perform final query on built table before returning result. Currently I implemented this as two functions, one accumulating and one performing final query, which is ugly:
CREATE OR REPLACE FUNCTION func_accu(LOCATION_ID INTEGER, SCHEMA_CUSTOMER TEXT)
RETURNS TABLE("networkid" integer, "count" bigint) AS $$
DECLARE
GATEWAY_ID integer;
BEGIN
FOR GATEWAY_ID IN
execute format(
'SELECT id FROM %1$I.gateway WHERE location_id=%2$L'
, SCHEMA_CUSTOMER, LOCATION_ID)
LOOP
RETURN QUERY execute format(
'SELECT * FROM get_available_networks_gw(%1$L, %2$L)'
, GATEWAY_ID, SCHEMA_CUSTOMER);
END LOOP;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION func_query(LOCATION_ID INTEGER, SCHEMA_CUSTOMER TEXT)
RETURNS TABLE("networkid" integer, "count" bigint) AS $$
DECLARE
BEGIN
RETURN QUERY execute format('
SELECT networkid, max(count) FROM func_accu(%2$L, %1$L) GROUP BY networkid;'
, SCHEMA_CUSTOMER, LOCATION_ID);
END;
$$ LANGUAGE plpgsql;
How can this be done in single function, elegantly?
Both functions simplified and merged, also supplying value parameters in the USING clause:
CREATE OR REPLACE FUNCTION pg_temp.func_accu(_location_id integer, schema_customer text)
RETURNS TABLE(networkid integer, count bigint) AS
$func$
BEGIN
RETURN QUERY EXECUTE format('
SELECT f.networkid, max(f.ct)
FROM %I.gateway g
, get_available_networks_gw(g.id, $1) f(networkid, ct)
WHERE g.location_id = $2
GROUP BY 1'
, _schema_customer)
USING _schema_customer, _location_id;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM func_accu(123, 'my_schema');
Related:
Dynamically access column value in record
I am using alias names for the columns returned by the function (f(networkid, ct)) to be sure because you did not disclose the return type of get_available_networks_gw(). You can use the column names of the return type directly.
The comma (,) in the FROM clause is short syntax for CROSS JOIN LATERAL .... Requires Postgres 9.3 or later.
What is the difference between LATERAL and a subquery in PostgreSQL?
Or you could run this query instead of the function:
SELECT f.networkid, max(f.ct)
FROM myschema.gateway g, get_available_networks_gw(g.id, 'my_schema') f(networkid, ct)
WHERE g.location_id = $2
GROUP BY 1;