I have a Type with below structure.
create type t_attr as (id character varying(50), data character varying(100));
I get text/varchar value through a user-defined function. The value looks like below.
txt := 'id1#data1' , 'id2#data2';
(In the above example, there are 2 values separated by comma, but the count will vary).
I'm trying to store each set into the t_attr array using the below code. Since each set will contain a # as separator I use it to split the id and data values. For testing purpose I have used only one set below.
DO
$$
DECLARE
attr_array t_attr[];
txt text := 'id1#data1';
BEGIN
attr_array[1] := regexp_split_to_array(txt, '#');
END;
$$
LANGUAGE plpgsql;
But the above code throws error saying 'malformed record literal' and 'missing left paranthesis'.
Can someone please help on how to store this data into array? Thanks.
It is because you are trying to assign an array value to the record. Try:
do $$
declare
attr_array t_attr[];
txt text := 'id1#data1';
begin
attr_array[1] := (split_part(txt, '#', 1), split_part(txt, '#', 2));
raise info '%', attr_array;
end $$;
Output:
INFO: {"(id1,data1)"}
DO
However if you really need to split values using arrays:
do $$
declare
a text[];
begin
....
a := regexp_split_to_array(txt, '#');
attr_array[1] := (a[1], a[2]);
end $$;
Related
I am trying to write a very simple pgsql statement to loop through a simple array of state abbreviations.
CREATE OR REPLACE FUNCTION my_schema.showState()
RETURNS text AS
$$
DECLARE
my_array text[] := '["az","al", "ak", "ar"]'
BEGIN
FOREACH state IN my_array
LOOP
RETURN SELECT format('%s', state);
END LOOP;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM showState();
I am using PostgresSQL version 11+. I keep getting an error ERROR: syntax error at or near "BEGIN" The output I want to see here is just seeing the state abbreviation printed in the results window for now. Like this:
What am I doing wrong here?
There's a ; missing after my_array text[] := '["az","al", "ak", "ar"]'.
'["az","al", "ak", "ar"]' isn't a valid array literal.
If you want a set returning function, you need to declare its return type as a SETOF.
The ARRAY keyword is missing in the FOREACH's head.
state must be declared.
You need to use RETURN NEXT ... to push a value into the set to be returned.
format() is pointless here, it doesn't effectively do anything.
With all that rectified one'd get something along the lines of:
CREATE
OR REPLACE FUNCTION showstate()
RETURNS SETOF text
AS
$$
DECLARE
my_array text[] := ARRAY['az',
'al',
'ak',
'ar'];
state text;
BEGIN
FOREACH state IN ARRAY my_array
LOOP
RETURN NEXT state;
END LOOP;
END;
$$
LANGUAGE plpgsql;
db<>fiddle
I have created a user-defined type in PostgreSQL(version 11.5).
CREATE TYPE key_type AS (key_no character varying(50), key_nm character varying(128));
I created below function with an input parameter of type key_type[] and output as TEXT.
create or replace function fn_net(inp IN key_type[]) returns TEXT as........
But I am unable to call the function, I have tried as below.
do
$$
declare
v_key key_type[];
v_res TEXT;
begin
v_key.key_no := '709R';
v_key.key_nm := 'Risk';
select * from fn_det(v_key) into v_res;
raise notice '%', v_res;
end;
$$
language plpgsql;
I get malformed array error or function does not exist error.
Please help me as to how to pass the inputs correctly.
NOTE: I am able to run successfully if I specify input type as key_type instead of key_type[] but I need array type for the requirement.
Your variable assignment is wrong, you need to provide the array index to which you want to assign an element.
When you are calling a function returning a single value, you don't need a SELECT in PL/pgSQL, just assign the result:
do
$$
declare
v_key key_type[];
v_res TEXT;
begin
v_key[1] := ('709R', 'Risk'); -- first array element
v_key[2] := ('711X', 'Risk2'); -- second array element
v_res := fn_det(v_key);
raise notice '%', v_res;
end;
$$
language plpgsql;
I am trying to write a function that does this: basically create an array with same data elements as the array that is passed to the function but with some change sometimes - like if some element is even then the flag should change from N to Y. The procedure takes as input an array that has two elements - a number and a flag. eg. (1,'N'), (2,'N'). Now if the passed number is even then the proc should modify that value and change to (2,'Y') whereas the other one remains as (1,'N').
Basically my array basics are not clear and reading through details has not helped so this question.
I tried the following but it is not working...can you please suggest:
CREATE TYPE test_n_t_num AS (
v_n double precision,
is_even character varying(1));
create function temp_n_proc_2(p_nums IN OUT test_n_t_num[])
as
$$
declare
v_nums test_n_t_num[];
v_cnt double precision;
BEGIN
v_cnt := cardinality(p_nums);
v_nums := ARRAY[]::test_n_t_num[];
for i in 1..v_cnt LOOP
if p_nums[i].v_n_double % 2 = 0 then
v_nums [i].is_even := 'Y';
p_nums [i].is_even := 'Y'
else
v_nums [i].is_even := p_nums [i].is_even;
end if;
v_nums[i] := {p_nums[i].v_n,v_nums [i].is_even};
END LOOP;
END;
$$
language plpgsql;
Also later I need to loop through and print out the values in the array v_nums - one that is defined in the function.
Thank you,
Nirav
I had a similar issue when I was trying to do this sort of thing back in the day. Basically you can't assign to composite objects using array notation, i.e. your_array[1].field := value doesn't work. Here's something that does (I don't use cardinality since I'm still on 9.3 and that was added in 9.4):
CREATE TYPE public.test1 AS (a INTEGER, is_even BOOLEAN);
CREATE OR REPLACE FUNCTION f1(ar INOUT public.test1[]) AS $$
DECLARE
t public.test1;
BEGIN
RAISE NOTICE '%', ar;
FOR i IN 1..ARRAY_LENGTH(ar, 1) LOOP
t := ar[i];
t.is_even := t.a % 2 = 0;
ar[i] := t;
END LOOP;
RAISE NOTICE '%', ar;
END
$$ LANGUAGE plpgsql;
So basically create a variable of that type, read the indexed item into the variable, modify the fields, then copy the variable's content back to the array.
SELECT * FROM f1('{"(1,)","(4,)","(6,)"}'::public.test1[]) returns {"(1,f)","(4,t)","(6,t)"}
The messages printed (this is using pgadmin 3) are:
NOTICE: {"(1,)","(4,)","(6,)"}
NOTICE: {"(1,f)","(4,t)","(6,t)"}
I want to create a function that can dynamically insert any data type value into any table and multiple columns. But my function can only insert one value to one column:
CREATE OR REPLACE FUNCTION public.dynamic_insert(
tablename character varying,
columname character varying,
datatype character varying,
valuee text
)
RETURNS void
LANGUAGE 'plpgsql'
COST 100
VOLATILE AS
$BODY$
begin
Execute format('insert into %I ('||columname||')
values (cast($1 as '||datatype||'))',tablename,columname);
end;
$BODY$;
Can any one help?
I don't understand your idea clearly. I guess that you want to write one function to insert into table with list of column and values you specify.
If I guess correctly. I suggest some function like this:
CREATE OR REPLACE FUNCTION public.dynamic_insert(
tablename character varying,
columname character varying[],
datatype character varying[],
valuee character varying[]
)
RETURNS void AS
$BODY$
DECLARE
var_sql varchar;
var_sql_column varchar := '';
var_sql_values varchar := '';
begin
var_sql_column := CONCAT('("',ARRAY_TO_STRING(columname, '","'),'")');
FOR i IN 1..ARRAY_LENGTH(columname,1)
LOOP
var_sql_values := var_sql_values || quote_literal(valuee[i]) || '::' || datatype[i] || ',';
END LOOP;
var_sql_values := CONCAT('(',regexp_replace(var_sql_values, ',$', '', 'g'),')');
var_sql := CONCAT('INSERT INTO ', quote_ident(tablename), var_sql_column, ' VALUES ', var_sql_values);
IF (var_sql IS NOT NULL) THEN
EXECUTE(var_sql);
END IF;
end;
$BODY$;
LANGUAGE 'plpgsql'
COST 100
VOLATILE;
Hopefully it matches your require.
p/s: with my experience, just choose one solution between Format, and String concatenation, don't use both in one query, it makes me hard to view and find bug.
------------- Update for question below -----------------------------------
For the Loop please refer the document here
Basically, it will run from 1 to n (with n is the number elements in array) and build a query.
One simple thing to view how it run is you can put some print out data into you function, such as RAISE. Please refer it document here
Created a view , my input will be in a comma separated , input contains only the column names of the view .
My input is '"Name","Description"' like this .
I tried like this
Select unnest(string_to_array('"Name","Description"',','))
From my_test_view
Unfortunately i got result like this
"Name"
"Description"
"Name"
"Description"
this only selecting my column names . I need json output with column values . my input is dynamic
Effectively, what you are trying to do is to dynamically use identifiers on a view for selecting data. You need to write a PL/pgSQL function to do that.
If you trust the input data (i.e. you are certain that the input only contains comma-separated column names) then the solution is very straightforward:
CREATE FUNCTION my_test_view_dynamic(columns text) RETURNS SET OF my_test_view AS $$
BEGIN
RETURN QUERY EXECUTE format('SELECT %s FROM my_test_view', columns);
END;
$$ LANGUAGE plpgsql STABLE STRICT;
If you are not so sure about the sanity of the input data, check it first:
CREATE FUNCTION my_test_view_dynamic(columns text) RETURNS SET OF my_test_view AS $$
DECLARE
c text;
safe_names text[] := '{}'::text[];
BEGIN
FOREACH c IN ARRAY regexp_split_to_array(columns, ',') LOOP
safe_names := safe_names || quote_identifier(c);
END LOOP;
RETURN QUERY EXECUTE format('SELECT %s FROM my_test_view', concat_ws(safe_names, ','));
END;
$$ LANGUAGE plpgsql STABLE STRICT;
Then call like:
SELECT * FROM my_text_view_dynamic('"Name","Description"');