Store multiple columns result query in array variable - postgresql

I would like to store the result of a query in a arraylist of a custom type.
The created type is:
create type statement_part AS (
statementid integer,
statement_value_date timestamp,
statement_last_transmission timestamp);
and the procedure code:
CREATE OR REPLACE FUNCTION public.get_bank_statements_downloadable_state
(
folderID integer,
date1 timestamp without time zone,
date2 timestamp without time zone
)
RETURNS bank_day[]
LANGUAGE plpgsql
AS $get_bank_statements_downloadable_state$
declare
statements_part_array statement_part[];
temp_statements_part_array statement_part[];
statements_part_element statement_part;
bank_day_array bank_day[];
bank_day_element bank_day;
nb_days integer;
begin
select array(bast_id::integer), array(bast_valuedate), array(bast_lasttransmission)
from bast_bankstatement
inner join baac_bankaccount
on bast_baac_id = baac_bankaccount.baac_id
where baac_folder_id = folderid
and (bast_valuedate between date1 and date2)
and bast_state = 1
into statements_part_array;
foreach statements_part_element in array statements_part_array loop
RAISE NOTICE 'Array list(%)', statements_part_array;
end loop;
nb_days := (select * from DATE_PART('day', AGE(date1, date2)) AS days);
for dayCounter in 0..nb_days-1 loop
--on met dans notre tableau temporaire les relevés banque valides du jour du cabinet
temp_statements_part_array :=
(
select *
from statements_part_array
where statements_part_array.statement_value_date = date1+dayCounter
);
if count(temp_statement_list) = 0 then
bank_day_element := (date1+dayCounter,0);
else
bank_day_element := (date1+dayCounter,2);
foreach statements_part_element in array temp_statements_part_array
loop
if statements_part_element.statement_last_transmission is null then
bank_day_element := (date1+dayCounter,1);
EXIT;
end if;
end loop;
end if;
bank_day_array := array_append(bank_day_array,bank_day_element);
end loop;
return bank_day_array;
END;
$get_bank_statements_downloadable_state$;
But it fails . It says the subquery must return only one column when I execute it.

I solved the issue by using
select array(select row(bast_id,bast_valuedate,bast_lasttransmission)
into statements_part_array
from bast_bankstatement
inner join baac_bankaccount
on bast_baac_id = baac_bankaccount.baac_id
where baac_folder_id = folderid
and (bast_valuedate between date1 and date2)
and bast_state = 1)
as statements_part_array;

Related

PostgreSQL Function returning only one row

I am writing a Function to accept a list by a parameter and return some set of records. When I run the select query alone, it is showing all the rows. But from Function I'm getting only the top row. Still searching about an hour, didn't get solutions.
Here is the Function query
CREATE OR REPLACE FUNCTION get_transaction_all_property(p_property character varying, p_year timestamp without time zone)
RETURNS SETOF record
LANGUAGE plpgsql
AS $function$
DECLARE
l_num_property numeric(15,2);
l_num_strtamt numeric(15,2);
l_num_endamt numeric(15,2);
l_property varchar := '';
l_year timestamp := LOCALTIMESTAMP;
result RECORD;
BEGIN
IF ( p_property IS NOT NULL ) THEN
l_property := p_property;
END IF;
IF ( p_year IS NOT NULL ) THEN
l_year := p_year;
END IF;
SELECT INTO l_num_property, l_num_strtamt, l_num_endamt
property, coalesce(sum(strtamt),0)::numeric(15,2), coalesce(sum(endamt),0)::numeric(15,2) from (
(select a.property as property, SUM(b.strtamtg + b.strtamtl) AS strtamt, SUM(b.endamtg + b.endamtl) AS endamt
FROM "myTransactions" AS a
WHERE a.property::text = ANY(STRING_TO_ARRAY(l_property,',')) AND a.period < l_year
group by a.property)
)as doo group by property;
SELECT INTO result l_num_property, l_num_strtamt, l_num_endamt;
RETURN next result;
END;
$function$
;
-- Permissions
ALTER FUNCTION get_transaction_all_property(varchar,timestamp,int8) OWNER TO mysuer;
GRANT ALL ON FUNCTION get_transaction_all_property(varchar,timestamp,int8) TO mysuer;
Here is the Function Call from SSRS:
select * from get_transaction_fund_totals_year_recon_sf_new(?,?) as ("property" numeric, "initial" numeric, "end" numeric)
SSRS Parameter Expression:
=Join(Parameters!pty.Value,",")
=Join(Parameters!dat.Value,",")
Please any one guide me to do this.
Thanks in Advance
The PL/pgSQL construct SELECT ... INTO will silently discard all but the first result rows.
Instead of doing this:
SELECT INTO l_num_property, l_num_strtamt, l_num_endamt ...;
SELECT INTO result l_num_property, l_num_strtamt, l_num_endamt;
RETURN next result;
do this:
RETURN QUERY SELECT ...;

How to compare two table value using if condition in function of Postgres

create or replace function trace.get_latest_exception_custom_msg(id varchar)
returns varchar
language plpgsql
as $$
declare
msg varchar ;
begin
perform t1.message, t1.created_time from table_1 t1 where t1.id = id order by t1.created_time desc limit 1;
perform t2.message, t2.created_time from table_2 t2 where t2.id = id order by t2.created_time desc limit 1;
if date(t1.created_time ) >= date(t2.created_time) then msg= t1.message;
elsif d date(t1.created_time ) < date(t2.created_time) then msg= t1.message;
else msg =t1.message;
end if;
return msg;
end;
while i call this function it give error ERROR: missing FROM-clause entry for table "t_1
You need to store the result of the two SELECT queries into variables in order to be able to be able to use them in an IF statement.
Your IF statement is also a bit confusing as all three parts assign the same value to msg. I assume that you want to use t2.message at least in one case.
create or replace function trace.get_latest_exception_custom_msg(p_id varchar)
returns varchar
language plpgsql
as
$$
declare
t1_msg varchar;
t1_created date;
t2_msg varchar;
t2_created date;
msg varchar;
begin
select t1.message, t1.created_time::date
into t1_msg, t1_created
from table_1 t1
where t1.id = p_id
order by t1.created_time desc
limit 1;
select t2.message, t2.created_time::date
into t2_msg, t2_created
from table_2 t2
where t2.id = p_id
order by t2.created_time desc
limit 1;
if t1_created >= t2_created then
msg := t1_msg;
elsif t1_created < t2_created then
msg := t2_msg; --<< ???
else
-- this can only happen if one (or both) of the DATEs is NULL.
msg := t1_msg;
end if;
return msg;
end;
$$

INTERVAL add i day is not working in postgresql

I have a table like this:
CREATE TABLE DateInsert(
DateInsert timestamp without time zone,
DateInt integer NOT NULL
);
I want insert list day from 2018-01-01 to 2045-05-18 but it give me an erro
"invalid input syntax for type interval:"
CREATE OR REPLACE FUNCTION insertdate() RETURNS integer AS $$
DECLARE i integer := 0;
d timestamp without time zone := '2018-01-01';
di integer := 0;
BEGIN
while i <10000
LOOP
d := d + INTERVAL ''+ i::character varying + ' day';
di := to_char(d , 'yyyymmdd')::int;
insert into DateInsert(DateInsert,DateInt) values(d, di);
i := i+1;
END LOOP ;
return i;
END;
$$ LANGUAGE plpgsql;
How can I insert to db with timestamp increase 1 in n day loop?
Code In sql server has been working.
declare #i int=0
declare #d datetime
declare #di int = 0
while #i <10000
begin
set #d = DATEADD(DAY, #i, '2018-01-01')
set #di = cast(CONVERT(VARCHAR(10), #d, 112) as int)
insert into DateInsert(DateInsert,DateInt) values(#d, #di)
set #i = #i+1
end
The concatenation operator is || not +. And the prefixed form doesn't seem to like anything else than literals. But you can cast the concatenation expression.
So changing
...
d := d + INTERVAL ''+ i::character varying + ' day';
...
to
...
d := d + (i || ' day')::interval;
...
should work for you.

Function is not returning table

Data is not returned by the function even though all calculations are done and i can see the messages in the console with the information.
If tried creating a record variable to store the values and return next for each row.
CREATE OR REPLACE FUNCTION recompra ()
RETURNS TABLE (
anho INTEGER,
cod_cliente INTEGER,
cliente VARCHAR(255),
cantidad INTEGER,
clasificacion VARCHAR(64)
) AS $$
DECLARE
anho INTEGER;
cod_cliente INTEGER;
cliente VARCHAR(255);
cantidad INTEGER;
clasificacion VARCHAR(64);
anhos_anteriores INTEGER;
BEGIN
FOR anho IN (SELECT DISTINCT(EXTRACT(YEAR FROM v.fecha_factura)) as anhos
FROM ventas_vehiculos as v
GROUP BY anhos
ORDER BY anhos ASC) LOOP
BEGIN
FOR cod_cliente , cliente IN (SELECT d.cod_cliente, CONCAT(TRIM(d.nombre),' ',TRIM(d.apellido))
FROM clientes as d WHERE estado_cliente <> 'Inactivo' LIMIT 10) LOOP
-- TRAE LAS VENTAS POR ANHO
BEGIN
FOR cantidad IN SELECT COUNT(f.nro_factura) FROM ventas_vehiculos as f
WHERE f.cliente = cod_cliente
AND EXTRACT(YEAR FROM f.fecha_factura) =anho LOOP
SELECT COUNT(f.nro_factura) FROM ventas_vehiculos as f
WHERE f.cliente = cod_cliente
AND extract(YEAR FROM f.fecha_factura) < anho INTO anhos_anteriores;
IF (cantidad >0 AND anhos_anteriores > 1) THEN
clasificacion = 'Recompra';
ELSIF (cantidad > 0 AND anhos_anteriores = 0) then
clasificacion = 'Compra';
ELSIF (cantidad =0) THEN
clasificacion = 'No compra';
RAISE NOTICE '% -- % -- % -- % -- % --', anho,cod_cliente,cliente,cantidad,clasificacion;
END IF;
END LOOP; --- cierra loop cantidades por anho
END; -- cierra trae ventas por anho
END LOOP; --- cierra for clientes
END; -- cierra begin clientes
END LOOP; -- CIERRA FOR ANHOS
END;
$$ LANGUAGE plpgsql;
expected output
year cod_cliente nombre cantidad clasificacion
2018 1234 Juan 12 compra
2016 3232 pedro 1 recompra
There are many problems with your code, I'll list those I can see right away:
There is no RETURN NEXT statement in your code. You'll have to have one for every row you want to return.
You declare local variables with the same name as the output parameters, which will lead to a name conflict. Don't do that. The return parameters in the RETURNS TABLE clause already are PL/pgSQL variables.
You should use := rather than = for assignment. This is the supported way and avoids confusion with the comparison operator.

How return SELECT * in my plpgsql

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.