Function is not returning table - postgresql

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.

Related

Store multiple columns result query in array variable

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;

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;
$$

PostgreSql cursorfunction for unique id

I am trying to Create a cursor on cartesian product/join as below, it gives an error
create or replace function som1() returns integer as $$
declare
rCur cursor for (select* from t1);
er route%rowtype;
begin
for er in
select route_id, location, happy from t1, t2 where exams.pid = route.pid
loop
end loop;
return 4;
end;
$$ language plpgsql;
select som1();

How to transfer a variable within a funtion in PostgreSQL

I'm running km_test.sql from a bat-file (Windows 7):
call psql -h ... -U ... -d ... -f C:\svn\tre2\prog\km_test.sql -v nrl=%a%
where %a% is an integer. I'm running on PostgreSQL 9.5.
km_test.sql looks like this
\set n :nrl
DROP FUNCTION km_test(integer);
CREATE FUNCTION km_test(n integer)
RETURNS void AS
$BODY$
DECLARE
j smallint;
BEGIN
DROP TABLE IF EXISTS km_test CASCADE;
CREATE UNLOGGED TABLE km_test (
lnr smallint,
km_id character varying(16),
flatenr smallint,
geo geometry(Linestring,25833)
);
j = 1;
WHILE j < n+1 LOOP
RAISE NOTICE 'Verdi j er : %', j;
INSERT INTO km_test (lnr, km_id, flatenr, geo)
SELECT d.i,
p.km_id,
CAST(substring(p.flatenr from 5 for 4) AS smallint),
ST_MakeLine(p.geo,(ST_Translate(p.geo, d.dx, d.dy)))
FROM org_tre2.km_punkter_des2016 AS p, org_tre2.km_dxdy1 AS d
WHERE j = d.i;
j = j + 1;
END LOOP;
COMMENT ON TABLE org_tre2.km_test IS 'KM innsyn: n innsynslinjer for kulturminnepunkt utenfor IK i tre2-flater';
END;
$BODY$
LANGUAGE plpgsql;
\set tab 'org_tre2.km_punkter_des2016'
select km_test(:n);
Question: How do I make table p (org_tre2.km_punkter_des2016) to be an input parameter? Meaning how to include the tablename in the function-call (with select km_tull(:n,:p)) and refer to this table within the function?
CREATE FUNCTION km_test(n integer, t text)
...
select km_test(:n,:'tab')
So far I have not managed to refer to the input-table as the t-variable in the FROM-statement.
Is it possible? Or is there a workaround?
Thanks for the hint/link #McNets. I think I've figured it out. Correct code in km_test.sql:
\set n :nrl
DROP FUNCTION km_test(integer, text);
CREATE FUNCTION km_test(n integer, t text)
RETURNS void AS
$BODY$
DECLARE
j smallint;
s integer;
BEGIN
DROP TABLE IF EXISTS km_test CASCADE;
CREATE UNLOGGED TABLE km_test (
lnr smallint,
km_id character varying(16),
flatenr smallint,
geo geometry(Linestring,25833)
);
j = 1;
WHILE j < n+1 LOOP
s:=j;
RAISE NOTICE 'Verdi j er : %', j;
RAISE NOTICE 'Tabell t er : %', t;
EXECUTE 'INSERT INTO km_test (lnr, km_id, flatenr, geo)
SELECT d.i,
p.km_id,
CAST(substring(p.flatenr from 5 for 4) AS smallint),
ST_MakeLine(p.geo,(ST_Translate(p.geo, d.dx, d.dy)))
FROM '||t||' as p, org_tre2.km_dxdy1 AS d
WHERE '||s||' = d.i';
j = j + 1;
END LOOP;
COMMENT ON TABLE org_tre2.km_test IS 'KM innsyn: n innsynslinjer for kulturminnepunkt utenfor IK i tre2-flater';
END;
$BODY$
LANGUAGE plpgsql;
\set ptab 'org_tre2.km_punkter_des2016'
select km_test(:n,:'ptab');
The solution being using EXECUTE, put all the INSER INTO-code within '', declare s as a variable and use s instead of j in the WHERE-clause.

Check constraint fire only on Insert and not on updates PostgresSQL

My Check constraint is as follows:
ALTER TABLE tablename
ADD CONSTRAINT check_duplicate_rows
CHECK (reject_duplicate_rows(columnB, columnC, columnD) < 2);
I want the constraint to be evaluated only when you insert a record.
Currently it does for both the insert and update statements, The problem is that my system needs to update the inserted rows and the check constraint blocks the updates.
The reject_duplicate_rows function is as follows:
CREATE OR REPLACE FUNCTION reject_duplicate_rows(columnB integer, columnC integer, columnD integer)
RETURNS integer AS
$BODY$
DECLARE
results INTEGER := 1;
v_count INTEGER := 0;
BEGIN
IF columnC <> 23 THEN
RETURN results;
END IF;
SELECT total INTO v_count FROM
(SELECT columnB,
columnC,
columnD,
count(*) AS total
FROM table_name
WHERE B = columnB AND C = columnC AND D = columnD
GROUP BY 1, 2, 3)
as temp_table;
IF COALESCE(v_count, 0) = 0 THEN
RETURN results;
END IF;
IF v_count >= 1 THEN
results := 2;
END IF;
RETURN results;
EXCEPTION
WHEN OTHERS THEN
RETURN results;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 1000;
ALTER FUNCTION reject_duplicate_rows(integer, integer, integer)
OWNER TO postgres
Have you tried to create an UPDATE trigger? see Creating postgresql trigger