This is an example but I want only a column with 1-10 values without other text columns.
CREATE OR REPLACE FUNCTION somefun_recordset(param_numcount integer)
RETURNS SETOF record AS
$$
DECLARE
result text := '';
searchsql text := '';
var_match record;
BEGIN
searchsql := 'SELECT n || '' down'' As countdown, n as integer
FROM generate_series(' || CAST(param_numcount As text) || ', 1, -1) As n ';
FOR var_match IN EXECUTE(searchsql) LOOP
RETURN NEXT var_match;
END LOOP;
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE;
SELECT r.n , r.countdown
FROM somefun_recordset(10)
As r(countdown text, n integer)
ORDER BY r.n;
How do I create a loop in postgres?
From your current description, you seem to be over-complicating this.
As described in the Postgres manual, the generate_series function can generate a column of values from 10 to 1, like so:
SELECT generate_series(10, 1, -1) as n;
Or using it as a table alias rather than a column alias:
SELECT * FROM generate_series(10, 1, -1) as n;
If you wanted to wrap this in a custom function with only one parameter, it would look like this:
CREATE FUNCTION countdown(param_numcount INT) RETURNS SETOF INT LANGUAGE SQL AS $$
SELECT generate_series(param_numcount, 1, -1);
$$;
Then you would run it as:
SELECT countdown(10) as n;
Related
I am trying to write function in postgresql, that creates temp_table with columns table_name text, table_rec jsonb and fill it through for loop with table names from my table containing names of tables and records in json. I have the for loop in string and I want to execute it. But it doesnt work.
I have variable rec record, sql_query text and tab_name text and I want to do this:
CREATE OR REPLACE FUNCTION public.test51(
)
RETURNS TABLE(tabel_name text, record_json jsonb)
LANGUAGE 'plpgsql'
COST 100
VOLATILE
ROWS 1000
AS $BODY$
declare
rec record;
tabel_name text;
tabel_names text[];
counter integer := 1;
sql_query text;
limit_for_sending integer;
rec_count integer;
begin
select into tabel_names array(select "TABLE_NAME" from public."TABLES");
create temp table temp_tab(tab_nam text, recik jsonb);
while array_length(tabel_names, 1) >= counter loop
tabel_name := '"' || tabel_names[counter] || '"';
select into limit_for_sending "TABLE_LIMIT_FOR_SENDING_DATA" from public."TABLES" where "TABLE_NAME" = tabel_name;
sql_query := 'select count(*) from public.' || tabel_name;
execute sql_query into rec_count;
if (rec_count >= limit_for_sending and limit_for_sending is not null) then
sql_query := 'for rec in select * from public.' || tabel_name || '
loop
insert into temp_tab
select ' || tabel_name || ', to_jsonb(rec);
end loop';
execute sql_query;
end if;
counter := counter + 1;
end loop;
return query
select * from temp_tabik;
drop table temp_tabik;
end;
$BODY$;
Thank you for response.
It seems you have some table that contains the information for which tables you want to return all rows as JSONB. And that meta-table also contains a column that sets a threshold under which the rows should not be returned.
You don't need the temp table or an array to store the table names. You can iterate through the query on the TABLES table and run the dynamic SQL directly in that loop.
return query in PL/pgSQL doesn't terminate the function, it just appends the result of the query to the result of the function.
Dynamic SQL is best created using the format() function because it is easier to read and using the %I placeholder will properly deal with quoted identifiers (which is really important as you are using those dreaded upper case table names)
As far as I can tell, your function can be simplified to:
CREATE OR REPLACE FUNCTION public.test51()
RETURNS TABLE(tabel_name text, record_json jsonb)
LANGUAGE plpgsql
AS
$BODY$
declare
rec record;
sql_query text;
rec_count bigint;
begin
for rec in
select "TABLE_NAME" as table_name, "TABLE_LIMIT_FOR_SENDING_DATA" as rec_limit
from public."TABLES"
loop
if rec.rec_limit is not null then
execute format('select count(*) from %I', rec.table_name)
into rec_count;
end if;
if (rec.rec_limit is not null and rec_count >= rec.rec_limit) then
sql_query := format('select %L, to_jsonb(t) from %I as t', rec.table_name, rec.table_name);
return query execute sql_query;
end if;
end loop;
end;
$BODY$;
Some notes
the language name is an identifier and should not be enclosed in single quotes. This syntax is deprecated and might be removed in a future version so don't get used to it.
you should really avoid those dreaded quoted identifiers. They are much more trouble than they are worth it. See the Postgres wiki for details.
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.
Trying to create a function that looks for sequence with particular name if does not exist should create it. Then returns function value of sequence. Part of function that does not seem to be working is where it tests if sequence already exists. Below is code for function
CREATE OR REPLACE FUNCTION public."tenantSequence"(
vtenantid integer,
vtablename character)
RETURNS bigint AS
$BODY$DECLARE
vSeqName character varying;
vSQL character varying;
BEGIN
select ('t' || trim(to_char(vtenantid,'0000')) || vtablename) INTO vSeqName;
if not exists(SELECT 0 FROM pg_class where relkind = 'S' and relname = vSeqName )
then
vSQL := 'create sequence '||vSeqName||';';
execute vSQL;
ELSE
return 0;
end if;
return nextval(vSeqName) * 10000 + vtenantid;
END$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION public."tenantSequence"(integer, character)
OWNER TO postgres;
Problem was case as mentioned by a_horse_with_no_name
changed line assign vSeqName to the following
vSeqName := lower('t' || trim(to_char(vtenantid,'0000')) || vtablename);
Now function works as expected.
I have integer array {0,23,1,29,0,15,1} postgres(V 9.3.6) and i want to remove particular element which highlighted above using it's index for example it's 3 as of now i get those element index using function idx
CREATE OR REPLACE FUNCTION idx(anyarray, anyelement)
RETURNS int AS
$$
SELECT i FROM (
SELECT generate_series(array_lower($1,1),array_upper($1,1))
) g(i)
WHERE $1[i] = $2
LIMIT 1;
$$ LANGUAGE sql IMMUTABLE;
But I'm unable to found any built in function in postgres which allows removal by using index.
create or replace function idx(the_array anyarray, idx integer)
returns anyarray as $$
select array_agg(a order by i)
from (
select
generate_series(1, array_upper(the_array, 1)),
unnest(the_array)
) s(i, a)
where i != idx
; $$ language sql;
select idx(array[0,23,1,29,0,15,1], 3);
idx
------------------
{0,23,29,0,15,1}
i need help in my plpgsql, must return a temporary table that has dynamic columns, how can I do this?
as the name of the columns may vary, I do not know how to finish this procedure
Sorry, google translator :D
CREATE OR REPLACE FUNCTION getreport(reportid INTEGER, userId VARCHAR)
RETURNS SETOF RECORD AS
$$
DECLARE
recordResultadoFinal RECORD;
recordResultadoNomeEspecificos RECORD;
varGetSqlRelatorio VARCHAR;
varAreaId queryreports.f_area%TYPE;
varClientId queryreports.f_client%TYPE;
varTableNameTemp VARCHAR := 'temp'||userId;
varSqlAlterTable VARCHAR := '';
varSqlUpdateTemp VARCHAR := '';
varNomeColunaSpecificData VARCHAR := '';
BEGIN
SELECT f_sql,f_area,f_client INTO varGetSqlRelatorio,varAreaId,varClientId FROM queryreports WHERE f_id = reportid;
EXECUTE 'DROP TABLE IF EXISTS '||varTableNameTemp;
EXECUTE 'CREATE TEMP TABLE '||varTableNameTemp||' AS '||varGetSqlRelatorio;
EXECUTE 'CREATE INDEX processid_idx ON '||varTableNameTemp||' USING btree (processid)';
FOR recordResultadoNomeEspecificos IN EXECUTE '
SELECT DISTINCT cs.f_id as idcoluna, cs.f_name as nomecoluna, cs.f_type as tipodado
FROM clientspecifics cs
INNER JOIN clientspecificdatas csd ON (cs.f_id = csd.f_clientspecific AND csd.f_process IN (SELECT processid FROM '||varTableNameTemp||'))
ORDER BY 2
'
LOOP
varSqlAlterTable := varSqlAlterTable||' ALTER TABLE '||varTableNameTemp||' ADD COLUMN specific_'||recordResultadoNomeEspecificos.idcoluna||' varchar;';
IF (recordResultadoNomeEspecificos.tipodado = 1) THEN varNomeColunaSpecificData := 'f_text';
ELSIF (recordResultadoNomeEspecificos.tipodado = 2) THEN varNomeColunaSpecificData := 'f_name';
ELSIF (recordResultadoNomeEspecificos.tipodado = 3) THEN varNomeColunaSpecificData := 'f_date';
ELSIF (recordResultadoNomeEspecificos.tipodado = 4) THEN varNomeColunaSpecificData := 'f_value';
ELSIF (recordResultadoNomeEspecificos.tipodado = 5) THEN varNomeColunaSpecificData := 'f_text';
END IF;
varSqlUpdateTemp := varSqlUpdateTemp||' UPDATE '||varTableNameTemp||' SET specific_'||recordResultadoNomeEspecificos.idcoluna||' = csd.'||varNomeColunaSpecificData||'
FROM clientspecificdatas csd
WHERE csd.f_process = processid
AND csd.f_clientspecific = '||recordResultadoNomeEspecificos.idcoluna||';';
END LOOP;
EXECUTE varSqlAlterTable;
EXECUTE varSqlUpdateTemp;
RETURN QUERY EXECUTE 'SELECT * FROM '||varTableNameTemp;
END;
$$ LANGUAGE 'plpgsql';
You have a few options:
You can return a refcursor and then fetch from that. This only works in transactions.
You can return an xml document and then process that in your app
You can return JSON or HSTORE.
You can return setof record and specify column lists in the function declaration but this is ugly and brittle.
The problem is that PostgreSQL needs to know return types when planning a query. this means you can't have things like jagged rows or dynamic numbers of rows. These have to be wrapped in something the planner can accommodate.