passing a list of varchars into plpgsql function - postgresql

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]

Related

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;

how to set a function parameter like a list

I created a function like the following one,
DROP FUNCTION if exists test_list_parameter();
CREATE or REPLACE FUNCTION test_list_parameter(???)
returns setof test
as
$$
select * from test
where test.id in ($1);
$$
language sql
how to set it for querying many ids at the same time?
like the following code,
we need to set the parameter when id more than one, which like (1,2,3,4...) in where clause.
thanks for your help.
Just pass an array of the datatype that of the id. If it's integer, pass a int[] . If it's text, use text[] etc. Also, use ANY instead of IN
CREATE or REPLACE FUNCTION test_list_parameter( idlist int [])
returns setof test
as
$$
select * from test
where test.id = ANY(idlist);
$$
language sql
Call it as
select * from test_list_parameter(ARRAY[1,3]);
DEMO

In clause in postgres

Need Output from table with in clause in PostgreSQL
I tried to make loop or ids passed from my code. I did same to update the rows dynamically, but for select I m not getting values from DB
CREATE OR REPLACE FUNCTION dashboard.rspgetpendingdispatchbyaccountgroupidandbranchid(
IN accountgroupIdCol numeric(8,0),
IN branchidcol character varying
)
RETURNS void
AS
$$
DECLARE
ArrayText text[];
i int;
BEGIN
select string_to_array(branchidcol, ',') into ArrayText;
i := 1;
loop
if i > array_upper(ArrayText, 1) then
exit;
else
SELECT
pd.branchid,pd.totallr,pd.totalarticle,pd.totalweight,
pd.totalamount
FROM dashboard.pendingdispatch AS pd
WHERE
pd.accountgroupid = accountgroupIdCol AND pd.branchid IN(ArrayText[i]::numeric);
i := i + 1;
end if;
END LOOP;
END;
$$ LANGUAGE 'plpgsql' VOLATILE;
There is no need for a loop (or PL/pgSQL actually)
You can use the array directly in the query, e.g.:
where pd.branchid = any (string_to_array(branchidcol, ','));
But your function does not return anything, so obviously you won't get a result.
If you want to return the result of that SELECT query, you need to define the function as returns table (...) and then use return query - or even better make it a SQL function:
CREATE OR REPLACE FUNCTION dashboard.rspgetpendingdispatchbyaccountgroupidandbranchid(
IN accountgroupIdCol numeric(8,0),
IN branchidcol character varying )
RETURNS table(branchid integer, totallr integer, totalarticle integer, totalweight numeric, totalamount integer)
AS
$$
SELECT pd.branchid,pd.totallr,pd.totalarticle,pd.totalweight, pd.totalamount
FROM dashboard.pendingdispatch AS pd
WHERE pd.accountgroupid = accountgroupIdCol
AND pd.branchid = any (string_to_array(branchidcol, ',')::numeric[]);
$$
LANGUAGE sql
VOLATILE;
Note that I guessed the data types for the columns of the query based on their names. You have to adjust the line with returns table (...) to match the data types of the select columns.

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;

Returning result set from Postgres functions

In my Postgres 9.2 database, I need to build a function that takes several parameters, performs several queries, and then returns a data set that is composed of several rows and several columns. I've built several test functions to get a better grasp of Postgres' functionality, here is one:
CREATE OR REPLACE FUNCTION sql_with_rows11(id integer) RETURNS character varying AS
$BODY$
declare vid integer;
declare vendor character varying;
BEGIN
vid := (select v_id from public.gc_alerts where a_id = id);
vendor := (select v_name from public.gc_vendors where v_id = vid);
RETURN vendor;
END;
$BODY$
LANGUAGE plpgsql;
I know that I can combine this into one query, but this is more of a practice exercise. This works fine and I get the vendor name. However, I need to return more than one column from the gc_vendors table.
Ultimately, I need to return columns from several tables based on subqueries. I've looked into creating a result set function, but I believe it only returns one row at a time. I also looked into returning setof type, but that seems to be limited to existing tables.
After initial feedback, I changed the function to the following:
CREATE OR REPLACE FUNCTION sql_with_rows14(IN v_uid character varying, IN lid integer)
RETURNS table (aid int, aname character varying) AS
$BODY$
declare aid integer;
declare aname character varying;
BEGIN
sql_with_rows14.aid := (select a_id from public.gc_alerts where v_id = sql_with_rows14.v_uid);
sql_with_rows14.aname := (select a_name from public.gc_alerts where a_id = sql_with_rows14.aid);
RETURN;
END;
$BODY$
LANGUAGE plpgsql;
I also tried RETURN NEXT, but same results.
When I query it, if the query returns only one row, it works fine. However it doesn't work for multiple rows. I also tried something like this, with the same result:
...
BEGIN
sql_with_rows14.aid := (select a_id from public.gc_alerts);
sql_with_rows14.aname := (select a_name from public.gc_alerts);
RETURN NEXT;
END;
I need to return more than one column from the gc_vendors table
To return a single row with multiple fields (as opposed to a set of rows), you can either use:
RETURNS row_type
.. where row_type is a pre-defined composite type (like a table name, that serves as such automatically). Or:
RETURNS record
combined with OUT parameters. Be aware that OUT parameters are visible in the body almost everywhere and avoid naming conflicts.
Using the second option, your function could look like this:
CREATE OR REPLACE FUNCTION sql_with_columns(IN _id integer -- IN is optional default
, OUT vid integer
, OUT vendor text)
RETURNS record
LANGUAGE plpgsql AS
$func$
BEGIN
SELECT INTO vid v_id
FROM public.gc_alerts
WHERE a_id = id;
SELECT INTO vendor v_name
FROM public.gc_vendors
WHERE v_id = vid;
RETURN; -- just noise, since OUT parameters are returned automatically
END
$func$;
As you mentioned, you should combine both queries into one, or even use a plain SQL statement instead. This is just a show case. The excellent manual has all the details.
You can also use:
RETURNS TABLE (...)
Or:
RETURNS SETOF row_type
This allows to return a set of rows (0, 1 or many). But that's not in your question.
To get individual columns instead of a record representation, call the function with:
SELECT * FROM sql_with_columns(...);
There are lots of examples here on SO, try a search - maybe with additional key words.
Also read the chapter "Returning from a Function" in the manual.
First of all, consider using views or simple queries. I'd say that if you can process something with a simple query, you shouldn't create function for that. in your case, you can use this query
select
v.v_name, v.* -- or any other columns from gc_alerts or gc_vendors
from public.gc_alerts as a
inner join public.gc_vendors as v on v.v_id = a.vid
where a.a_id = <your id here>
if you want your function to return rows, you can declare it like
CREATE OR REPLACE FUNCTION sql_with_rows11(id integer)
RETURNS table(vendor text, v_id int)
as
$$
select
v.v_name, v.v_id
from public.gc_alerts as a
inner join public.gc_vendors as v on v.v_id = a.vid
where a.a_id = id
$$ language SQL;
or plpgsql function:
CREATE OR REPLACE FUNCTION sql_with_rows11(id integer)
RETURNS table(vendor text, vid int)
AS
$$
declare vid integer;
declare vendor character varying;
BEGIN
sql_with_rows11.vid := 1; -- prefix with function name because otherwise it would be declared variables
sql_with_rows11.vendor := 4;
return next;
sql_with_rows11.vid := 5;
sql_with_rows11.vendor := 8;
return next;
END;
$$ LANGUAGE plpgsql;
sql fiddle demo to fiddle with :)