Use Variable in a select statement in a Function - postgresql

Is it possible to declare a variable from another variable that was already declared in a function?
Example of what I am expecting below..
CREATE OR REPLACE FUNCTION insertRecord
(
a varchar (100),
b varchar(100),
c varchar(100)
)
RETURNS TEXT AS $$
DECLARE
orgId := (select id from org)
projectId := select id from project where orgId = orgId
BEGIN
return projectId;
END;
$$ LANGUAGE plpgsql;

First declare the variables. And rename the orgId to avoid ambiguity with the column name.
CREATE OR REPLACE FUNCTION insertRecord
(
a varchar (100),
b varchar(100),
c varchar(100)
)
RETURNS TEXT AS $$
DECLARE
orgId_var integer;
projectId varchar;
BEGIN
orgId_var := (select id from org limit 1);
projectId := (select id from project where orgId = orgId_var);
return projectId;
END;
$$ LANGUAGE plpgsql;

Perhaps you just want to use a join instead? Something along these lines (but with more filters added so as not to return multiple rows):
CREATE OR REPLACE FUNCTION insertRecord(
a varchar (100),
b varchar(100),
c varchar(100)
)
RETURNS TEXT AS $$
DECLARE
v_project_id project.id%TYPE;
BEGIN
SELECT p.id
INTO v_project_id
FROM project p
JOIN org o ON (o.id = p.org_id);
RETURN v_project_id;
END;
$$ LANGUAGE plpgsql;

Related

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

create child table function using cursor in postgresql

CREATE FUNCTION create_child1()
RETURNS TABLE(sys_user_id integer,
sys_service_id integer
)
LANGUAGE 'plpgsql'
COST 100
VOLATILE
ROWS 1000
AS $BODY$
DECLARE
curr_id CURSOR IS
SELECT id FROM users WHERE id in (3089,3090,3091,3092);
v_id bigint;
BEGIN
OPEN curr_id;
LOOP
FETCH curr_id INTO v_id;
EXIT WHEN not found ;
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I (
sys_user_id integer,
sys_service_id integer
id bigint NOT NULL primary key
)
INHERITS (telemetry_master)
WITH (
OIDS=FALSE
)', 'telemetry_' || v_id);
end loop;
close curr_id;
fetch next from curr_id into v_id;
END
$BODY$ LANGUAGE plpgsql;
You do not need an explicit cursor in your function. You can use a simple FOR ... IN ... LOOP.
It is unclear what you want to return from the function. For example, it can return a readable text about each created table.
CREATE OR REPLACE FUNCTION create_child1()
RETURNS SETOF text LANGUAGE plpgsql
AS $BODY$
DECLARE
v_id int;
BEGIN
FOR v_id IN 3089..3092 LOOP
EXECUTE format('
CREATE TABLE IF NOT EXISTS telemetry_%s (
sys_user_id integer,
sys_service_id integer,
id bigint NOT NULL primary key
)
INHERITS (telemetry_master)', v_id);
RETURN NEXT format('telemetry_%s created.', v_id);
END LOOP;
END $BODY$;
Use:
SELECT create_child1();
create_child1
-------------------------
telemetry_3089 created.
telemetry_3090 created.
telemetry_3091 created.
telemetry_3092 created.
(4 rows)
If the ids are not consecutive you can use unnest(), e.g.:
FOR v_id IN SELECT id FROM unnest(array[3000,3001,3020,3021]) AS id LOOP

How to Return a Table That Was Created Within Function?

I have a PL/pgSQL function that creates a table. I would like to return the table that was created as part of a "Return Table" function. Here is an example of the type of function I'm talking about:
CREATE OR REPLACE FUNCTION my_schema.new_foo_table(character varying)
RETURNS table(row_id bigint,
catalog_id varchar,
value numeric(5,4)) AS
$BODY$
DECLARE
v_table_name ALIAS FOR $1;
cmd text;
BEGIN
cmd := 'CREATE TABLE '||v_table_name||'
(row_id bigint,
catalog_id varchar,
value numeric(5,4))';
EXECUTE cmd;
cmd := 'INSERT INTO '||v_table_name||'
SELECT row_id, catalog_id, sum(value)
FROM other_table
GROUP BY row_id, catalog_id';
EXECUTE cmd;
RETURN --results of select * from v_table_name;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
How would I return the results of the new table I created? I'm guess the return would have to use some type of dynamic SQL "execute" statement since the name of the newly created table is in a variable.
Yes this can be done using RETURN QUERY EXECUTE 'command string'. For your example it would look like this:
CREATE OR REPLACE FUNCTION my_schema.new_foo_table(character varying)
RETURNS table(row_id bigint,
catalog_id varchar,
value numeric(5,4)) AS
$BODY$
DECLARE
arg_table_name ALIAS FOR $1;
v_table_name varchar;
cmd text;
BEGIN
v_table_name := quote_ident(arg_table_name);
cmd := 'CREATE TABLE '||v_table_name||'
(row_id bigint,
catalog_id varchar,
value numeric(5,4))';
EXECUTE cmd;
cmd := 'INSERT INTO '||v_table_name||'
SELECT row_id, catalog_id, sum(value)
FROM other_table
GROUP BY row_id, catalog_id';
EXECUTE cmd;
RETURN QUERY EXECUTE 'select * from v_table_name';
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
For additional information see section 40.6.1.2. in the POSTGRESQL documentation: http://www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html#PLPGSQL-STATEMENTS-RETURNING

Greenplum/Postgres 8 function dynamic result set?

I need to write a function that returns a table with unknown number of columns.
If i receive 'None' in column input parameter then that column shouldn't be included in the output. In postgres 9+ there is a solution for this problem.
something like below:
CREATE OR REPLACE FUNCTION data_of(id integer,col1 varchar,col2 varchar, col3 varchar)
RETURNS TABLE (count_rec, dimensions text[] ) AS
$func$
DECLARE
_dimensions text := 'col1, col2, col3'; -- If i receive 'None' in input param then i exclude that from column list
BEGIN
RETURN QUERY EXECUTE format('
SELECT count(*) as count_rec,
string_to_array($1) -- AS dimensions
FROM x
WHERE id = $2'
, _dimensions)
USING _dimensions , _id;
END
$func$ LANGUAGE plpgsql;
But in Greenplum (Postgres 8.2) i could not find any. Is there any similar solution?
thanks
You have 2 options to do it: use set-returning function returning "record" or returning your custom type.
First option:
create table test (a int, b int, c int, d varchar, e varchar, f varchar);
insert into test select id, id*2, id*3, (id*4)::varchar, (id*4)::varchar, (id*4)::varchar from generate_series(1,10) id;
create or replace function test_func(column_list varchar[]) returns setof record as $BODY$
declare
r record;
begin
for r in execute 'select ' || array_to_string(column_list, ',') || ' from test' loop
return next r;
end loop;
return;
end;
$BODY$
language plpgsql
volatile;
select * from test_func(array['a','c','e']) as f(a int, c int, e varchar);
Second option:
create table test (a int, b int, c int, d varchar, e varchar, f varchar);
insert into test select id, id*2, id*3, (id*4)::varchar, (id*4)::varchar, (id*4)::varchar from generate_series(1,10) id;
create type testtype as (
a int,
c int,
e varchar
);
create or replace function test_func() returns setof testtype as $BODY$
declare
r testtype;
begin
for r in execute 'select a,c,e from test' loop
return next r;
end loop;
return;
end;
$BODY$
language plpgsql
volatile;
select * from test_func();
But I'm 99% sure you're trying to do something wrong. In Greenplum the result of function execution cannot be used as a "table" in join conditions, because the function executes on the master. You even won't be able to create a table out of the last query returning the data from your function because of this limitation
In short, this is not a recommended way to work with data in Greenplum

POSTGRESQL convert recursive function to loop function

CREATE OR REPLACE FUNCTION employee(IN emp_id integer)
RETURNS TABLE(id integer, name text, designation text, salary integer, man_id integer) AS
$BODY$
BEGIN
RETURN QUERY
WITH recursive manager_hierarchy(e_id,e_name,e_desig,e_sal,m_id) AS
(
select e.id,e.name,e.designation,e.salary,e.man_id FROM emp_table e
WHERE e.id=emp_id
union
SELECT
rp.id,rp.name,rp.designation,rp.salary,rp.man_id
FROM
manager_hierarchy mh
INNER JOIN emp_table rp ON mh.e_id=rp.man_id
)
SELECT h.e_id,h.e_name,h.e_desig,h.e_sal,h.m_id
FROM manager_hierarchy h;
END;
$BODY$
LANGUAGE plpgsql
can anyone help me to change the same program with using loop