postgres function - declare variable with select statement - postgresql

i have modifed my function, but i have problems with declare variables. I used postgres 8.4. Have someone an idea?
Function:
CREATE OR REPLACE FUNCTION requestcounterid(_mindate timestamptz, _maxdate timestamptz)
RETURNS TABLE (kategorien text, requestcounter int) AS
$func$
DECLARE
_minid bigint;
_maxid bigint;
BEGIN
_minid := (SELECT id from tablename where starttime >= $1 ORDER BY tablename2 ASC LIMIT 1);
_maxid := (SELECT id from tablename where starttime < $2 ORDER BY tablename2 DESC LIMIT 1);
SELECT CASE WHEN duration <= 10000000 THEN '00-01 sec'::text
WHEN duration <= 40000000 THEN '01-04 sec'
WHEN duration <= 100000000 THEN '04-10 sec'
WHEN duration <= 300000000 THEN '10-30 sec'
WHEN duration <= 600000000 THEN '30-60 sec'
ELSE 'more than 60 sec' END
, count(*)::int
FROM tablename
WHERE id >= _minid and id <= _maxid
GROUP BY 1
ORDER BY 1;
END;
$func$ LANGUAGE plpgsql;
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 "requestcounterid" line 11 at SQL statement
Regrads

Now it workes:
CREATE OR REPLACE FUNCTION requestcounterid(_mindate timestamptz, _maxdate timestamptz)
RETURNS TABLE (kategorien text, requestcounter int) AS
$func$
DECLARE
_minid bigint;
_maxid bigint;
BEGIN
SELECT id INTO _minid from tablename where starttime >= $1 ORDER BY starttime ASC LIMIT 1;
SELECT id INTO _maxid from tablename where starttime < $2 ORDER BY starttime DESC LIMIT 1;
Return Query SELECT CASE WHEN duration <= 10000000 THEN '00-01 sec'::text
WHEN duration <= 40000000 THEN '01-04 sec'
WHEN duration <= 100000000 THEN '04-10 sec'
WHEN duration <= 300000000 THEN '10-30 sec'
WHEN duration <= 600000000 THEN '30-60 sec'
ELSE 'more than 60 sec' END
, count(*)::int
FROM tablename
WHERE id >= _minid and id <= _maxid
GROUP BY 1
ORDER BY 1;
END;
$func$ LANGUAGE plpgsql;

Related

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;

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.

I can't subtract days from current_date parametrically in PostgreSQL

I want to subtract some days from the current date and insert it into a table. If I write the number of days directly into the code it works. So this works.
do $$
DECLARE
myDate Date;
BEGIN
myDate = current_date - interval '10' day;
insert into myTable (myDate) values (myDate);
end $$;
The problem is that I want to put the number of days in a variable to make it parametric. But I can't. In fact the following doesn't work:
do $$
DECLARE
myDate Date;
daysAgo character varying := '10';
BEGIN
myDate = current_date - interval daysAgo day;
insert into myTable (myDate) values (myDate);
end $$;
You can subtract a number of days (integer) from a date:
do $$
DECLARE
myDate Date;
daysAgo int := 10;
BEGIN
myDate = current_date - daysAgo;
insert into myTable (myDate) values (myDate);
end $$;
Date/Time Functions and Operators.
Alternatively if you need to do this not just for days, you can do certain math operations on intervals:
do $$
declare
minutes int4 := 34;
begin
raise notice '%', interval '1 minute' * minutes;
raise notice '%', interval '15 minute' / minutes;
end;
$$;
-- outpus:
-- 00:34:00
-- 00:00:26.470588
This also works (with the make_interval function) but is more verbouse than Klin's solution.
do $$
DECLARE
myDate Date;
daysAgo int := 10;
BEGIN
myDate = current_date - make_interval(days=>daysAgo);
insert into myTable (myDate) values (myDate);
end $$;

Return table type from a function in PostgreSQL

I have a function with RETURNS TABLE, and I want to return certain columns from my source table. When I execute the function, it gives no error but also returns no rows although it should.
What's wrong with my function?
CREATE OR REPLACE FUNCTION ccdb.fn_email_details_auto()
RETURNS TABLE (code integer, area smallint, action smallint
, flag smallint, ucount integer, view_cnt integer) AS
$BODY$
DECLARE
sec_col refcursor;
cnt integer;
sec_code ccdb.update_qtable%ROWTYPE;
BEGIN
SELECT COUNT(DISTINCT section_code) INTO cnt
FROM ccdb.update_qtable
WHERE entry_time::date = now()::date - interval '1 day';
OPEN sec_col FOR
SELECT * FROM ccdb.update_qtable
WHERE entry_time::date = now()::date - interval '1 day';
FOR i IN 1..cnt
LOOP
FETCH sec_col INTO sec_code;
PERFORM section_code, ddu_area, ddu_action, status_flag
, ccdb_ucount, ccdb_view_cnt
FROM ccdb.update_qtable
WHERE entry_time::date = now()::date - interval '1 day'
AND section_code = sec_code.section_code
ORDER BY ddu_area, ddu_action;
END LOOP;
CLOSE sec_col;
END;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
Your function is doing a lot of empty work.
You could replace the tedious and expensive explicit cursor with a FOR loop using a cursor implicitly. But don't bother, and radically simplify with a single query instead. Optionally wrapped into an SQL function:
CREATE OR REPLACE FUNCTION ccdb.fn_email_details_auto()
RETURNS TABLE (code integer, area smallint, action smallint, flag smallint
, ucount integer, view_cnt integer)
LANGUAGE sql AS
$func$
SELECT u.section_code, u.ddu_area, u.ddu_action, u.status_flag
, u.ccdb_ucount, u.ccdb_view_cnt
FROM ccdb.update_qtable u
WHERE u.entry_time >= now()::date - 1
AND u.entry_time < now()::date -- sargable!
ORDER BY u.section_code, u.ddu_area, u.ddu_action;
$func$;
Should be much faster while returning the same.
Also, use this:
WHERE u.entry_time >= now()::date - 1
AND u.entry_time < now()::date
instead of:
WHERE entry_time::date = now()::date - interval '1 day'
The alternative is "sargable" and can use a plain index on (entry_time), which is crucial for performance.
I was able to solve this issue by using a RETURN QUERY for the SELECT statement where I was using PERFORM.
The below mentioned query helped me achieve my requirement.
CREATE OR REPLACE FUNCTION ccdb.fn_email_details_auto()
RETURNS TABLE (code integer, area smallint, action smallint, flag smallint, ucount integer, view_cnt integer) AS
$BODY$
DECLARE
sec_col refcursor;
cnt integer;
sec_code ccdb.update_qtable%ROWTYPE;
BEGIN
SELECT COUNT(DISTINCT section_code)
INTO cnt
FROM ccdb.update_qtable
WHERE entry_time::date = now()::date - interval '1 day';
OPEN sec_col FOR
SELECT DISTINCT ON (section_code)* FROM ccdb.update_qtable WHERE entry_time::date = now()::date - interval '1 day';
FOR i IN 1..cnt
LOOP
FETCH sec_col INTO sec_code;
RETURN QUERY
SELECT section_code, ddu_area, ddu_action, status_flag, ccdb_ucount, ccdb_view_cnt
FROM ccdb.update_qtable
WHERE entry_time::date = now()::date - interval '1 day' AND section_code = sec_code.section_code
ORDER BY ddu_area, ddu_action;
END LOOP;
CLOSE sec_col;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;