Postgres function returns ERROR of no destination - postgresql

Here is my function for my DB:
create function calculate_metrics()
returns table
(
fail_count int,
hold_pay_count int
)
as
$$
declare
total_count int;
fail_count int;
hold_pay_count int;
begin
select count(1) as total_count,
sum(case when status = 'FAIL' then 1 else 0 end) as fail_count,
sum(case when status = 'HOLD_PAY' then 1 else 0 end) as hold_pay_count
from bundle where updated_at > (now() - interval '1 day');
if total_count > 0
then
return query select fail_count, hold_pay_count;
end if;
end;
$$ language plpgsql;
When I'm trying to get select calculate_metrics(), I'm getting the error:
SQL Error [42601]: ERROR: query has no destination for result data
Suggestion: If you want to discard the results of a SELECT, use PERFORM instead.
Where: PL/pgSQL function calculate_metrics() line 10 at SQL statement
The function is successfully create, and I have already insert data which consists the conditions of the requests. Where is my mistake?

Naming the column is not enough, you need to save the output into your variables
select count(1) as total_count,
sum(case when status = 'FAIL' then 1 else 0 end) as fail_count,
sum(case when status = 'HOLD_PAY' then 1 else 0 end) as hold_pay_count
INTO total_count, fail_count, hold_pay_count
from bundle where updated_at > (now() - interval '1 day');

Related

Incorporating a LOOP into a SQL

I am fairly new at SQL and have not incorporated a Loop into a SQL statement previously. This SQL query from elan.elig returns data as shown in the grid below.
select (extract(year from age(case when terminationdate is null then
CURRENT_DATE else terminationdate END ,effectivedate ))) *12 +
(extract(month from age(case when terminationdate is null then
CURRENT_DATE else terminationdate END ,effectivedate )) +1)
as "mbrmonths" ,effectivedate
from elan.elig
Mbr Months Effective Date
1 10/1/2018
10 11/1/2018
2 11/1/2018
8 11/1/2018
8 11/1/2018
8 11/1/2018
2 11/1/2018
2 11/1/2018
7 11/1/2018
For each row from the query I need to execute the subsequent LOOP that spreads the memberMonth counts into the Year/Month buckets. The following Do LOOP does exactly this. I have been trying for some time now to determine how to incorporate the Loop into the SQL statement so that for each row read, it will pass the two variables and execute the Loop and then read the next row and continue on..
DO $$
declare
nbr_mem_months integer=5;
effectivedate date ='20190401';
ym char(6) =to_char(effectivedate,'YYYYMM');
begin
for r in 1..nbr_mem_months loop
update elan.pmpm set mbrmonths=mbrmonths+1 where yyyyymm=ym;
effectivedate=effectivedate + interval '1 month';
ym=to_char(effectivedate,'YYYYMM');
end loop;
end;
$$;
PMPM Buckets
yyyymm mbrmonths
201901 0
201902 0
201903 0
201904 1
201905 1
201906 1
201907 1
201908 1
201909 0
201910 0
201911 0
CREATE FUNCTION "UpdatePMPM"() RETURNS boolean
LANGUAGE plpgsql
AS
$$
DECLARE
nbr_mem_months NUMERIC;
effectivedate date;
ym char(6);
BEGIN
LOOP
ym=to_char(effectivedate,'YYYYMM');
nbr_mem_months=5;
UPDATE elan.pmpm set mbrmonths=mbrmonths+1 where yyyyymm=ym;
effectivedate=effectivedate + interval '1 month';
END LOOP;
RETURN TRUE;
END
$$;
*Response from the Select statement:
ERROR: function public.UpdatePMPM(integer, date, text) does not exist
Select public."UpdatePMPM"(5,cast('20190101' as date),cast('...
^
HINT: No function matches the given name and argument types.
The issue is calling the function with arguments but not specifying any when creating the function. So you need something like(not tested):
CREATE FUNCTION "UpdatePMPM"(nbr_mem_months integer, effectivdate date, some_arg varchar) RETURNS void
LANGUAGE plpgsql
AS
$$
DECLARE
ym varchar := to_char(effectivedate,'YYYYMM');
BEGIN
FOR r IN 1..nbr_mem_months LOOP
UPDATE elan.pmpm set mbrmonths = mbrmonths+1 where yyyyymm = ym;
effectivedate = effectivedate + interval '1 month';
ym=to_char(effectivedate,'YYYYMM');
END LOOP;
RETURN;
END
$$;
From the error it is not clear what the third argument is supposed to be, so that will clarification from you.

Calling a Function from SQL

This is my Function :
CREATE FUNCTION "UpdatePMPM"(nbr_mem_months integer, effectivedate date) RETURNS void
LANGUAGE plpgsql
AS
$$
DECLARE
ym varchar := to_char(effectivedate,'YYYYMM');
BEGIN
FOR r IN 1..nbr_mem_months LOOP
UPDATE elan.pmpm set mbrmonths = mbrmonths+1 where yyyyymm = ym;
effectivedate = effectivedate + interval '1 month';
ym=to_char(effectivedate,'YYYYMM');
END LOOP;
RETURN;
END
$$;
and when I call it manually from pgAdmin client it works perfectly.
Select public."UpdatePMPM"(5, '2016-04-01')
However, I am getting an error when calling it from within a SQL query:
select cast((extract(year from age(case when terminationdate is null then
CURRENT_DATE else terminationdate END ,effectivedate ))) *12 +
(extract(month from age(case when terminationdate is null then
CURRENT_DATE else terminationdate END ,effectivedate )) +1) as integer)
as "mbrmonths" ,effectivedate ,public."UpdatePMPM"(mbrmonths, effectivedate)
from elan.elig
order by 1
ERROR: column "mbrmonths" does not exist
LINE 5: ...s "mbrmonths" ,effectivedate ,public."UpdatePMPM"(mbrmonths,...
Any help would be appreciated.
You cannot use a column alias in the SELECT list to reference another column of the same list.
Either repeat the expression or use a subquery:
SELECT mbrmonths,
effectivedate,
public."UpdatePMPM"(mbrmonths, effectivedate)
FROM (select cast(
(extract(year from age(case when terminationdate is null
then CURRENT_DATE
else terminationdate
END,
effectivedate)
)) *12 +
(extract(month from age(case when terminationdate is null
then CURRENT_DATE
else terminationdate
END,
effectivedate
)) +1
as integer) as "mbrmonths",
effectivedate,
from elan.elig) AS subq
order by 1;
The type of second argument is text, not date.
The Correct call is
Select public."UpdatePMPM"(5, '2016-04-01'::date)

Retutring from a function using case

Here is my postgres function:
create or replace function fail_hold_pay_count_metric()
returns table (
fail_count int,
hold_pay_count int
)
as
$$
declare
total_count int;
fail_count int;
hold_pay_count int;
required_rows_count int;
percent_count int;
begin
select count(1) as total_count,
sum(case when status = 'FAIL' then 1 else 0 end) as fail_count,
sum(case when status = 'HOLD_PAY' then 1 else 0 end) as hold_pay_count
into total_count, fail_count, hold_pay_count
from bundle where updated_at > (now() - interval '1 year');
if total_count > 10
then
required_rows_count := (select fail_count + hold_pay_count);
percent_count := (select (required_rows_count / total_count * 100));
end if;
return query (select case when (percent_count > 50) then (fail_count, hold_pay_count) else (0, 0) end);
end;
$$ language plpgsql;
I need to return (0,0) if percent_count was lower than 50. My function doesn't work. Where did I make a mistake?
You are returning a single column of a row type instead of a row of two int types.
Please try this, instead:
return query select case when percent_count > 50 then fail_count else 0 end,
case when percent_count > 50 then hold_pay_count else 0 end;

Database functions mapped to ActiveRecord Models

Let's suppose I have two database functions like this (pl/pgsql):
CREATE TABLE balances (rents decimal(12,2) DEFAULT 0 NOT NULL,
expenses decimal(12,2) DEFAULT 0 NOT NULL);
CREATE FUNCTION account_balance(id integer)
RETURNS balances AS $$
SELECT
SUM(CASE WHEN t.amount > 0 THEN t.amount ELSE 0 END) as rents,
SUM(CASE WHEN t.amount < 0 THEN t.amount ELSE 0 END) as expenses
FROM transactions t
WHERE t.account_id = $1
$$ language 'sql';
CREATE FUNCTION tenant_balance(id integer)
RETURNS balances AS $$
SELECT
SUM(CASE WHEN t.amount > 0 THEN t.amount ELSE 0 END) as rents,
SUM(CASE WHEN t.amount < 0 THEN t.amount ELSE 0 END) as expenses
FROM transactions t
WHERE t.tenant_id = $1
$$ language 'sql';
How could I relate to this in my ActiveRecord models (in this examples, Account and Tenant) so I can call something like Account.first.balance and it returns a valid instance of Balance?
Speaking of Balance, how should I create this model?

How to call Postgres function returning SETOF record?

I have written the following function:
-- Gets stats for all markets
CREATE OR REPLACE FUNCTION GetMarketStats (
)
RETURNS SETOF record
AS
$$
BEGIN
SELECT 'R approved offer' AS Metric,
SUM(CASE WHEN M.MarketName = 'A+' AND M.Term = 24 THEN LO.Amount ELSE 0 end) AS MarketAPlus24,
SUM(CASE WHEN M.MarketName = 'A+' AND M.Term = 36 THEN LO.Amount ELSE 0 end) AS MarketAPlus36,
SUM(CASE WHEN M.MarketName = 'A' AND M.Term = 24 THEN LO.Amount ELSE 0 end) AS MarketA24,
SUM(CASE WHEN M.MarketName = 'A' AND M.Term = 36 THEN LO.Amount ELSE 0 end) AS MarketA36,
SUM(CASE WHEN M.MarketName = 'B' AND M.Term = 24 THEN LO.Amount ELSE 0 end) AS MarketB24,
SUM(CASE WHEN M.MarketName = 'B' AND M.Term = 36 THEN LO.Amount ELSE 0 end) AS MarketB36
FROM "Market" M
INNER JOIN "Listing" L ON L.MarketID = M.MarketID
INNER JOIN "ListingOffer" LO ON L.ListingID = LO.ListingID;
END
$$
LANGUAGE plpgsql;
And when trying to call it like this...
select * from GetMarketStats() AS (
Metric VARCHAR(50),
MarketAPlus24 INT,
MarketAPlus36 INT,
MarketA24 INT,
MarketA36 INT,
MarketB24 INT,
MarketB36 INT);
I get an error:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function "getmarketstats" line 2 at SQL statement
I don't understand this output. I've tried using perform too, but I thought one only had to use that if the function doesn't return anything.
Your function doesn't maken sense, it doesn't return anything. It looks like a VIEW, so why don't you create a view?
Edit:
You have use the OUT parameters or RETURN TABLE() with the parameters:
CREATE OR REPLACE FUNCTION my_func(OUT o_id INT, OUT o_bar TEXT)
RETURNS SETOF RECORD AS
$$
BEGIN
RETURN QUERY SELECT id, bar FROM foo;
END;
$$
LANGUAGE plpgsql;
SELECT * FROM my_func();