How to get the value of var cast in this query postgresql? - postgresql

I have the problem when create select insert loop in procedure postgresql. The problem is var cast('1000/1902/003' AS TEXT) always null. How to detect this variabel? i realy need this variabel.
I have try without casting but the parameter always read as integer.
BEGIN
FOR tx IN EXECUTE 'SELECT * FROM T_DataUpload2_FinalDetail WHERE dataupload2fd_id = CAST('|| 1000/1902/003 ||'AS TEXT)'
LOOP
data_cust := tx.DataUpload2FD_DistID;
data_dist := tx.DataUpload2FD_CustID;
RETURN NEXT;
END LOOP;
END;

Why dynamic SQL to begin with?
BEGIN
FOR tx IN SELECT *
FROM T_DataUpload2_FinalDetail
WHERE dataupload2fd_id = '1000/1902/003'
LOOP
data_cust := tx.DataUpload2FD_DistID;
data_dist := tx.DataUpload2FD_CustID;
RETURN NEXT;
END LOOP;
END;
The expression (without quotes) 1000/1902/003 means "1000 divided by 1902 divided by 3" the result would be 0,175... which is rounded to 0 (not null) because all values are integers and thus integer division is used.

Related

How to code an atomic transaction in PL/pgSQL

I have multiple select, insert and update statement to complete a transaction, but I don't seem to be able to ensure all statements to be successful before committing the changes to table.
The transaction doesn't seem to be atomic.
I do have begin and end in my function but transaction doesn't seem to be atomic.
CREATE FUNCTION public.testarray(salesid integer, items json) RETURNS integer
LANGUAGE plpgsql
AS $$
declare
resu text;
resu2 text := 'TH';
ssrow RECORD;
oserlen int := 0;
nserlen int := 0;
counter int := 0;
begin
select json_array_length(items::json->'oserial') into oserlen;
while counter < nserlen loop
select items::json#>>array['oserial',counter::text] into resu;
select * into strict ssrow from salesserial where fk_salesid=salesid and serialnum=resu::int;
insert into stockloct(serialnum,fk_barcode,source,exflag) values(ssrow.serialnum,ssrow.fk_barcode,ssrow.fk_salesid,true);
counter := counter + 1;
end loop;
counter := 0;
select json_array_length(items::json->'nserial') into nserlen;
while counter < nserlen loop
select items::json#>>array['nserial',counter::text,'serial'] into resu2;
select * into ssrow from stockloc where serialnum=resu2::int;
insert into salesserial(fk_salesid,serialnum,fk_barcode) values(salesid,ssrow.serialnum,ssrow.fk_barcode);
counter := counter + 1;
end loop;
select items::json#>'{nserial,0,serial}' into resu2;
return resu;
end;
$$;
Even when the first insert fails, the second insert seems to be able to succeed.
I see that by “fail” you mean “does not insert any rows”.
That is not surprising since the first loop is never executed: both counter and nserlen are always 0.
Perhaps you mean the first WHILE condition to be counter < oserlen?
You also seem to br confused by PL/pgSQL's BEGIN: while it looks like the BEGIN that starts a transaction, it is quite different. It is just the “opening parenthesis” in a PL/pgSQL block.

How to pass and array and modify it in a function in postgres

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)"}

replacing values of specific index in postgresql 9.3

CREATE OR REPLACE FUNCTION array_replace(INT[]) RETURNS float[] AS $$
DECLARE
arrFloats ALIAS FOR $1;
J int=0;
x int[]=ARRAY[2,4];
-- xx float[]=ARRAY[2.22,4.33];
b float=2.22;
c float=3.33;
retVal float[];
BEGIN
FOR I IN array_lower(arrFloats, 1)..array_upper(arrFloats, 1) LOOP
FOR K IN array_lower(x, 1)..array_upper(x, 1) LOOP
IF (arrFloats[I]= x[K])THEN
retVal[j] :=b;
j:=j+1;
retVal[j] :=c;
j:=j+1;
ELSE
retVal[j] := arrFloats[I];
j:=j+1;
END IF;
END LOOP;
END LOOP;
RETURN retVal;
END;
$$ LANGUAGE plpgsql STABLE RETURNS NULL ON NULL INPUT;
When I run this query
SELECT array_replace(array[1,20,2,5]);
it give me output like this
"[0:8]={1,1,20,20,2.22,3.33,2,5,5}"
Now I do not know why it is coming this duplicate values. I mean it is straight away a nested loop ...
I need a output like this one
"[0:8]={1,20,2.22,3.33,5}"
You have a double loop with the x array having two elements. On every iteration you push elements onto the result array, hence you get twice as many values.
If I understand you logic correctly, you want to scan the input array for values of another array in that same order. If the same, then replace these values with another array, leaving other values intact. There are no built-in functions to help you here, so you have to do this from scratch:
CREATE FUNCTION array_replace(arrFloats float[]) RETURNS float[] AS $$
DECLARE
searchArr float[] := ARRAY[1.,20.];
replaceArr float[] := ARRAY[1.11,1.,111.,20.2,20.222];
retVal float[];
i int;
ndx int;
len int;
upp int;
low int
BEGIN
low := array_lower(searchArr, 1)
upp := array_upper(searchArr, 1);
len := upp - low + 1;
i := array_lower(arrFloats, 1);
WHILE i <= array_upper(arrFloats, 1) LOOP -- Use WHILE LOOP so can update i
ndx := i; -- index into arrFloats for inner loop
FOR j IN low .. upp LOOP
IF arrFloats[ndx] != searchArr[j] THEN
-- No match so put current element of arrFloats in the result and update i
retVal := retVal || arrFloats[i];
i := i + 1;
EXIT; -- No need to look further, break out of inner loop
END IF;
ndx := ndx + 1;
IF j = upp THEN
-- We have a match so append the replaceArr to retVal and
-- increase i by length of search_array
retVal := retVal || replaceArr;
i := i + len;
END IF;
END LOOP;
END LOOP;
RETURN retVal;
END;
$$ LANGUAGE plpgsql STABLE STRICT;
This function would become much more flexible if you made searchArr and replaceArr into parameters as well.
Test
patrick#puny:~$ psql -d test
psql (9.5.0, server 9.4.5)
Type "help" for help.
test=# select array_replace(array[1,20,2,5]);
array_replace
------------------------------
{1.11,1,111,20.2,20.222,2,5}
(1 row)
test=# select array_replace(array[1,20,2,5,1,20.1,1,20]);
array_replace
------------------------------------------------------------
{1.11,1,111,20.2,20.222,2,5,1,20.1,1.11,1,111,20.2,20.222}
(1 row)
As you can see it works for multiple occurrences of the search array.

Postgres - Dynamically referencing columns from record variable

I'm having trouble referencing record variable type columns dynamically. I found loads of tricks online, but with regards to triggers mostly and I really hope the answer isn't "it can't be done"... I've got a very specific and simple need, see my example code below;
First I have an array containing a list of column names called "lCols". I loop through a record variable to traverse my data, replacing values in a paragraph which exactly match my column names.
DECLARE lTotalRec RECORD;
DECLARE lSQL text;
DECLARE lCols varchar[];
p_paragraph:= 'I am [names] and my surname is [surname]';
lSQL :=
'select
p.names,
p.surname
from
person p
';
FOR lTotalRec IN
execute lSQL
LOOP
-- Loop through the already created array of columns to replace the values in the paragraph
FOREACH lVal IN ARRAY lCols
LOOP
p_paragraph := replace(p_paragraph,'[' || lVal || ']',lTotalRec.lVal); -- This is where my problem is, because lVal is not a column of lTotalRec directly like this
END LOOP;
RETURN NEXT;
END LOOP;
My return value is the paragraph amended for each record in "lTotalRec"
You could convert your record to a json value using the row_to_json() function. Once in this format, you can extract columns by name, using the -> and ->> operators.
In Postgres 9.4 and up, you can also make use of the more efficient jsonb type.
DECLARE lJsonRec jsonb;
...
FOR lTotalRec IN
execute lSQL
LOOP
lJsonRec := row_to_json(lTotalRec)::jsonb;
FOREACH lVal IN ARRAY lCols
LOOP
p_paragraph := replace(p_paragraph, '[' || lVal || ']', lJsonRec->>lVal);
END LOOP;
RETURN NEXT;
END LOOP;
See the documentation for more details.
You can convert a row to JSON using row_to_json(), and then retrieve the column names using json_object_keys().
Here's an example:
drop table if exists TestTable;
create table TestTable (col1 text, col2 text);
insert into TestTable values ('a1', 'b1'), ('a2', 'b2');
do $$declare
sql text;
rec jsonb;
col text;
val text;
begin
sql := 'select row_to_json(row) from (select * from TestTable) row';
for rec in execute sql loop
for col in select * from jsonb_object_keys(rec) loop
val := rec->>col;
raise notice 'col=% val=%', col, val;
end loop;
end loop;
end$$;
This prints:
NOTICE: col=col1 val=a1
NOTICE: col=col2 val=b1
NOTICE: col=col1 val=a2
NOTICE: col=col2 val=b2
DO

using Array_append gives me syntax error when creating PostgreSQL function

Here is the code
CREATE OR REPLACE FUNCTION primes (IN integer) RETURNS TEXT AS $$
DECLARE
counter INTEGER = $1;
primes int [];
mycount int;
BEGIN
WHILE counter != 0 LOOP
mycount := count(primes);
array_append(primes [counter], mycount);
counter := counter - 1;
END LOOP;
RETURN array_to_text(primes[], ',');
END;
$$
LANGUAGE 'plpgsql'
This is me developing the beginnings of a prime generating function. I am trying to simply get it to return the 'count' of the array. So if I pass '7' into the function I should get back [0, 1, 2, 3, 4, 5, 6].
But when I try to create this function I get
SQL Error: ERROR: syntax error at or near "array_append" LINE 1: array_append( $1 [ $2 ], $3 )
^ QUERY: array_append( $1 [ $2 ], $3 ) CONTEXT: SQL statement in PL/PgSQL function "primes" near line 8
I am a newbie with postgres functions. I am not understanding why I cannnot get this array to work properly.
Again all I want is to just fill this array up correctly with the 'current' count of the array. (It's more to just help me understand that it is in fact doing the loop correctly and is counting it correctly).
Thank you for your help.
From the fine manual:
Function: array_append(anyarray, anyelement)
Return Type: anyarray
Description: append an element to the end of an array
So array_append returns an array and you need to assign that return value to something. Also, I think you want array_to_string at the end of your function, not array_to_text. And primes is an array so you want array_append(primes, mycount) rather than trying to append to an entry in primes.
CREATE OR REPLACE FUNCTION primes (IN integer) RETURNS TEXT AS $$
DECLARE
counter INTEGER = $1;
primes int [];
mycount int;
BEGIN
WHILE counter != 0 LOOP
mycount := count(primes);
primes := array_append(primes, mycount);
counter := counter - 1;
END LOOP;
RETURN array_to_string(primes, ',');
END;
$$ LANGUAGE 'plpgsql';
I don't know what you intend mycount := count(primes); to do, perhaps you meant to say mycount := array_length(primes, 1); so that you would get a sequence of consecutive integers in primes.