column reference "col_1" is ambiguous plpgsql - postgresql

I'm writing postgresql function which inserts data and returns successfully inserted data rows.
The code is below.
CREATE OR REPLACE FUNCTION public.fn_insert_test(json_data jsonb)
returns table(col_1 varchar(255),
col_2 varchar(255),
col_3 timestamp)
LANGUAGE plpgsql
AS $function$
declare
--
begin
with my_table as (
with my_table1(my_json) as (
values(
json_data
)
)
insert into "test"(col_1, col_2, col_3)
select
elem->>'val1', elem->>'val2', now()
from
my_table1 t, jsonb_array_elements(my_json->'_data') elem
on conflict(col_1) do nothing
returning *
)
select * from my_table;
end
$function$
;
select fn_insert_test('{"_data": [{"val1":"1", "val2":"1"}, {"val1":"2", "val2":"2"}]}');
It occurs error below.
SQL Error [42702]: ERROR: column reference "col_1" is ambiguous
Detail: It could refer to either a PL/pgSQL variable or a table column.
Where: PL/pgSQL function fn_insert_test(jsonb) line 5 at SQL statement
[edit]

This is not the real problem here. There is a lot of problems in your code :
-> If you want to return a table, you have to use 'return query' : http://www.postgresqltutorial.com/plpgsql-function-returns-a-table/
-> I do not think you can use the WITH like you do: https://www.postgresql.org/docs/current/queries-with.html
Here is a functoinnal version of your code. But I am not quite sure of what you want :
CREATE OR REPLACE FUNCTION public.fn_insert_test(json_data jsonb)
returns table(col_1 varchar(255),
col_2 varchar(255),
col_3 timestamp)
LANGUAGE plpgsql AS
$$
declare
--json_data jsonb := '{"_data": [{"val1":"1", "val2":"1"}, {"val1":"2", "val2":"2"}]}';
begin
create temp table res (col_1 varchar (255), col_2 varchar (255), col_3 timestamp) on commit drop;
with my_table1(my_json) as
(
values(
json_data
)
)
, inserted as
(
insert into test(col_1, col_2, col_3)
select
elem->>'val1', elem->>'val2', now()
from
my_table1 t
, jsonb_array_elements(json_data->'_data') elem
on conflict do nothing
returning *
)
insert into res
select *
from inserted;
--raise notice '%', res_v;
return query select * from res;
end
$$
;
select fn_insert_test('{"_data": [{"val1":"1", "val2":"1"}, {"val1":"2", "val2":"2"}]}');

Related

Select from a table, add a parameter to the result and return it from a function

I have a function that returns the result of a select based on a parameter being passed to it. I would like to join the parameter to the returned result of the function. Here's an example:
create or replace function somefunc(param varchar)
returns table(fielda varchar, fieldb int, param varchar)
language plpgsql
as $$
begin
return query
select
fielda varchar,
fieldb int,
param varchar -- HOW DO I SELECT IT?
from
sometable
join
othertable on id = other_id
end;
$$
You just need to call the parameter correctly
I created a fake sometable table and inserted two rows with
create table sometable(fielda varchar, fieldb int);
insert into sometable values('a',1);
insert into sometable values('b',2);
Then specified the somefunc function with
create or replace function somefunc(param_in varchar)
returns table(fielda varchar, fieldb int, param varchar)
language plpgsql
as $$
begin
return query
select
sometable.fielda,
sometable.fieldb,
param_in
from sometable;
end;
$$
Check out the param_in input parameter not in contrast with the param field in the table. I removed the join with the othertable but that can also be added.

Merge Statement with temp table

CREATE OR REPLACE FUNCTION public.merge_test (
r_obj refcursor,
_ldeptid character varying
) RETURNS refcursor
LANGUAGE 'plpgsql' COST 100.0 VOLATILE AS $function$
BEGIN
DROP TABLE IF EXISTS tblCumulate;
create temp table tblCumulate (
lCompid varchar(10),
lOpenCount int default 0,
lClosedCount int default 0
);
DROP TABLE IF EXISTS tblOpen;
create temp table tblOpen (
lOSID SERIAL,
lCount numeric(24,0),
lCompid varchar(100)
);
MERGE into tblCumulate CUM using (select lcompid,lCount from tblopen) as OP
on CUM.lcompid=OP.lcompid
when matched
then update set cum.lOpenCount=op.lcount
when not matched
then insert (lCompid,lOpenCount) values op.lcompid,op.lcount);
open r_obj for
select * from tblCumulate;
return r_obj;
END;
$function$;
when I execute (Run) this procedure showing following error.
ERROR: "tblcumulate" is not a known variable
LINE 41: MERGE into tblCumulate CUM temp
There is no MERGE statement in PostgreSQL.
Consider using INSERT ... ON CONFLICT.

PostgreSQL 9.3: missing FROM-clause entry for table

I have a table with two columns.
Example:
create table t1
(
cola varchar,
colb varchar
);
Now I want to insert the rows from function.
In the function: I want to use two parameters which is of type varchar to insert the values into the above table. I am passing the string to insert into the table.
I am passing two string of characters as a parameters to the function:
Parameters:
cola varchar = 'a,b,c,d';
colb varchar = 'e,f,g,h';
The above parameters have to insert into the table like this:
cola colb
----------------
a e
b f
c g
d h
My try:
create or replace function fun_t1(cola varchar,colb varchar)
returns void as
$body$
Declare
v_Count integer;
v_i integer = 0;
v_f1 text;
v_cola varchar;
v_colb varchar;
v_query varchar;
Begin
drop table if exists temp_table;
create temp table temp_table
(
cola varchar,
colb varchar
);
v_Count := length(cola) - length(replace(cola, ',', ''));
raise info '%',v_Count;
WHILE(v_i<=v_Count) LOOP
INSERT INTO temp_table
SELECT LEFT(cola,CHARINDEX(',',cola||',',0)-1)
,LEFT(colb,CHARINDEX(',',colb||',',0)-1);
cola := overlay(cola placing '' from 1 for CHARINDEX(',',cola,0));
colb := overlay(colb placing '' from 1 for CHARINDEX(',',colb,0));
v_i := v_i + 1;
END LOOP;
for v_f1 IN select * from temp_table loop
v_cola := v_f1.cola; /* Error occurred here */
v_colb := v_f1.colb;
v_query := 'INSERT INTO t1 values('''||v_cola||''','''||v_colb||''')';
execute v_query;
end loop;
end;
$body$
language plpgsql;
Note: In the function I have used temp_table that is according to the requirement which
I am using for the other use also in the function which I have not display here.
Calling function:
SELECT fun_t1('a,b,c','d,e,f');
Getting an error:
missing FROM-clause entry for table "v_f1"
Try this way using split_part() : -
create or replace function ins_t1(vala varchar,valb varchar,row_cnt int) returns void as
$$
BEGIN
FOR i IN 1..row_cnt LOOP -- row_cnt is the number rows you need to insert (ex. 4 or 5 or whatever it is)
insert into t1 (cola,colb)
values (
(select split_part(vala,',',i))
,(select split_part(valb,',',i))
);
END LOOP;
END;
$$
language plpgsql
function call :select ins_t1('a,b,c,d','e,f,g,h',4)
As mike-sherrill-cat-recall said in his answer by using regexp_split_to_table
create or replace function fn_t1(vala varchar,valb varchar) returns void
as
$$
insert into t1 (cola, colb)
select col1, col2 from (select
trim(regexp_split_to_table(vala, ',')) col1,
trim(regexp_split_to_table(valb, ',')) col2)t;
$$
language sql
function call :select fn_t1('a,b,c,d','e,f,g,h')
If there's no compelling reason to use a function for this, you can just split the text using a regular expression. Here I've expressed your arguments as a common table expression, but that's just for convenience.
with data (col1, col2) as (
select 'a, b, c, d'::text, 'e, f, g, h'::text
)
select
trim(regexp_split_to_table(col1, ',')) as col_a,
trim(regexp_split_to_table(col2, ',')) as col_b
from data;
col_a col_b
--
a e
b f
c g
d h
If there is a compelling reason to use a function, just wrap a function definition around that SELECT statement.
create function strings_to_table(varchar, varchar)
returns table (col_a varchar, col_b varchar)
as
'select trim(regexp_split_to_table($1, '','')),
trim(regexp_split_to_table($2, '',''));'
language sql
stable
returns null on null input;
select * from strings_to_table('a,b,c,d', 'e,f, g, h');
col_a col_b
--
a e
b f
c g
d h
My personal preference is usually to build functions like this to return tables rather than inserting into tables. To insert, I'd usually write a SQL statement like this.
insert into foo (col_a, col_b)
select col_a, col_b from strings_to_table('a,b,c,d', 'e,f,g,h');
The simpest way is using plpython for this.
create or replace function fill_t1(cola varchar, colb varchar) returns void as $$
for r in zip(cola.split(','), colb.split(',')):
plpy.execute(plpy.prepare('insert into t1(cola, colb) values ($1, $2)', ['varchar', 'varchar']), [r[0], r[1]])
$$ language plpythonu;
The result:
# create table t1 (cola varchar, colb varchar);
CREATE TABLE
# select fill_t1('1,2,3', '4,5,6');
fill_t1
---------
(1 row)
# select * from t1;
cola | colb
------+------
1 | 4
2 | 5
3 | 6
(3 rows)
You can read about Python zip function here: https://docs.python.org/2/library/functions.html#zip

Update table being returned in PostgreSQL

I'm looking for help with a PostgreSQL function that returns a table.
I would like to know if there is a way of updating the table that is being returned.
Here's an example,
create or replace function fn_function()
return table(column01 integer, column02 integer, column03 boolean) as $$
return query
select col1, col2, false
from tableXYZ;
/* how can i update the table, let's say column03 before the function exits */
end
$$ language plpgsql;
Can i give an alias to the table being returned?
The postgre version in use is 9.0.8.
Thx in advance.
Here is a set based solution.
The RETURNS TABLE is just portal to get data out you can't do anything inside the function that defines it. You could create another function and call this one and do things to the result set. However I use a temp table so you can manipulate the data before you send it out.
CREATE OR REPLACE FUNCTION fn_function()
RETURNS TABLE(column01 integer, column02 integer, column03 boolean) AS $$
BEGIN
CREATE TEMP TABLE temp_tableXYZ (column01 integer, column02 integer, column03 boolean) ON COMMIT DROP;
INSERT INTO temp_tableXYZ (column01, column02, column03)
SELECT col1,col2,col3
FROM tableXYZ;
--WHERE filter if you can.
UPDATE temp_tableXYZ
SET col1 = 9999;
RETURN QUERY select column01, column02, column03 from temp_tableXYZ;
END;
$$ LANGUAGE plpgsql;
You can call it using an alias like this:
SELECT * FROM fn_function() as my_table;
Just do a query which returns the values you want. It can be plain sql:
create or replace function fn_function()
returns table (
column01 integer, column02 integer, column03 boolean
) as $$
select col1, col2, col2 > 10
from tableXYZ;
$$ language sql;
In the example above column 3 will be true if col2 > 10 and false otherwise. Another example using a subselect:
create or replace function fn_function()
returns table (
column01 integer, column02 integer, column03 boolean
) as $$
select col1, col2, (select max(col1) > 10 from t where col2 = tableXYZ.col1)
from tableXYZ;
$$ language sql;
Notice that it is not return but returns
To select from table, modify the results of select and pass them as results of a function try something like:
create or replace function fn_function()
returns table (
column01 integer, column02 integer, column03 boolean
) as $$
begin
for rec in select col1, col2, false as col3
from tableXYZ;
loop
rec.col3 := col1 > col2;
return next rec;
end loop;
return;
end
$$ language plpgsql;
Details here.
To select get the results of this function simply
SELECT function_alias.col1, function_alias.col2
FROM fn_function() function_alias;
You can do with this function same things you can do with normal tables.

postgres stored procedure problem

Ich have a problem in postgres function:
CREATE OR REPLACE FUNCTION getVar(id bigint)
RETURNS TABLE (repoid bigint, suf VARCHAR, nam VARCHAR)
AS $$
declare rec record;
BEGIN
FOR rec IN
(WITH RECURSIVE children(repoobjectid,variant_of_object_fk, suffix, variantname) AS (
SELECT repoobjectid, variant_of_object_fk, '' as suffix,variantname
FROM b2m.repoobject_tab
WHERE repoobjectid = id
UNION ALL
SELECT repo.repoobjectid, repo.variant_of_object_fk, suffix || '..' , repo.variantname
FROM b2m.repoobject_tab repo, children
WHERE children.repoobjectid = repo.variant_of_object_fk)
SELECT repoobjectid,suffix,variantname FROM children)
LOOP
RETURN next;
END LOOP;
RETURN;
END;
It can be compiled, but if y try to call it
select * from getVar(18)
I got 8 empty rows with 3 columns.
If i execute the following part of procedure with hard-coded id parameter:
WITH RECURSIVE children(repoobjectid,variant_of_object_fk, suffix, variantname) AS (
SELECT repoobjectid, variant_of_object_fk, '' as suffix,variantname
FROM b2m.repoobject_tab
WHERE repoobjectid = 18
UNION ALL
SELECT repo.repoobjectid, repo.variant_of_object_fk, suffix || '..' , repo.variantname
FROM b2m.repoobject_tab repo, children
WHERE children.repoobjectid = repo.variant_of_object_fk)
SELECT repoobjectid,suffix,variantname FROM children
I got exactly, what i need 8 rows with data:
repoobjectid suffix variantname
18
19 .. for IPhone
22 .. for Nokia
23 .... OS 1.0
and so on.
What is going wrong ? Please help.
Thanx in advance
I think if you're doing "return table", you need to assign to the "columns" of the table before doing "return next". So something like:
repoid := rec.repoid;
suf := rec.suf;
nam := rec.nam;
just before your "RETURN NEXT". Since you're not assigning these, they're being returned as null.
Here is a sample code of a funcion that returns rows from a table, I think it might help.
First, a sample table with sample data:
CREATE TABLE sample_table (id smallint, description varchar, primary key (id));
INSERT INTO sample_table (id, description) VALUES (1, 'AAAA');
INSERT INTO sample_table (id, description) VALUES (2, 'BBBB');
INSERT INTO sample_table (id, description) VALUES (3, 'CCCC');
INSERT INTO sample_table (id, description) VALUES (4, 'DDDD');
INSERT INTO sample_table (id, description) VALUES (5, 'EEEE');
Then, a return type that describes the fields of the rows returned:
CREATE TYPE return_type AS
(id smallint,
description varchar);
ALTER TYPE return_type OWNER TO postgres;
Then the function itself:
CREATE OR REPLACE FUNCTION report(p_id integer)
RETURNS SETOF return_type AS
$BODY$
DECLARE
retorno return_type%ROWTYPE;
BEGIN
FOR RETORNO IN SELECT * FROM sample_table WHERE id = p_id LOOP
RETURN NEXT RETORNO;
END LOOP;
RETURN;
END
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION report(p_id integer) OWNER TO postgres;
And here goes the function call:
SELECT * FROM report(1);