PostgreSQL stored procedure data parameter - postgresql

I have the following stored procedure, which returns 0 results but if the run the query by itself it result lot of results. What am i missing.
CREATE OR REPLACE FUNCTION countStatistics(baselineDate Date) RETURNS int AS $$
DECLARE
qty int;
BEGIN
SELECT COUNT(*) INTO qty FROM statistics WHERE time_stamp = baselineDate;
RETURN qty;
END;
$$ LANGUAGE plpgsql;
--Execute the function
SELECT countStatistics('2015-01-01 01:00:00') as qty;
return 0 results
SELECT COUNT(*) FROM statistics WHERE time_stamp = '2015-01-01 01:00:00';
return 100+ results

You're declaring your baselineDate parameter as a date:
CREATE OR REPLACE FUNCTION countStatistics(baselineDate Date)
but using it as a timestamp:
SELECT COUNT(*) INTO qty FROM statistics WHERE time_stamp = baselineDate;
You're getting an implicit cast so countStatistics('2015-01-01 01:00:00') will actually execute this SQL:
SELECT COUNT(*) INTO qty FROM statistics WHERE time_stamp = '2015-01-01';
and, after the date is implicitly cast back to a timestamp, it will effectively be this:
SELECT COUNT(*) INTO qty FROM statistics WHERE time_stamp = '2015-01-01 00:00:00';
Try changing your function declaration to use a timestamp:
CREATE OR REPLACE FUNCTION countStatistics(baselineDate timestamp)

Related

postgres function returns operator doesn't exist integer - timestamp

Hi I have a function that when calling it to test returns the error in the question. i know it is to do with how i insert the variable into part of the query but i cannot work out why, i've tried changing the type castings around on it but no luck. any help would be appreciated
create or replace function ageing_balance_calc(start_date text, client_id text) returns void
AS $BODY$
declare
_absd date;
_client_id text;
req text;
BEGIN
_absd:=(to_date(start_date,'YYYY-MM-DD'));
_client_id:= client_id;
with cte as
(
Select * from crosstab('
select * from (
select absd,
case when a.days_ago between 0 and 30 then ''0-30''
when a.days_ago between 31 and 60 then ''31-60''
when a.days_ago between 61 and 90 then ''61-90''
when a.days_ago >90 then ''90+''
else ''not due'' end as bucket, sum(a.item_amount)
from (select date_part(''day'',((due_date::timestamp) - ('||_absd||' ::text::timestamp)) )as days_ago, item_amount, '||_absd||'::text::date as absd from fdw_test) a
group by bucket, absd)b order by 1,2') as (absd date,
bucket0_30 numeric,
bucket31_60 numeric,
bucket61_90 numeric,
bucket90_plus numeric,
not_due numeric)
)
update ddva.kpi_calc_results
set
absd = a.absd,
bucket0_30 = a.bucket0_30,
bucket31_60 = a.bucket31_60,
bucket61_90 = a.bucket61_90,
bucket90_plus = a.bucket90_plus,
not_due = a.not_due from (
select absd, bucket0_30, bucket31_60, bucket61_90, bucket90_plus, not_due from cte) as a
where ddva.kpi_calc_results.client_id = _client_id ;
END;
$BODY$
LANGUAGE plpgsql
update
the date_part works if i remove it to its own query and then use $date to insert the date as a string that way. if i remove the to_date part at the start of the function i still get the same error as before. does '||x||' do anything to the data type?
the problem is here:
the result query looks like this: (2021-07-07::text::timestamp) and results in error: iteger - timestamp.
You must add more quotes: ('''||_absd||'''::text::timestamp)
Well:
date_part(''day'',((due_date::timestamp)
is going to return an integer:
select date_part('day', now());
27
I'm guessing you want something more like:
select now()::date; 2021-07-27
UPDATE
This part:
'''||_absd||'''::text::timestamp
won't work either:
select '''||_absd||'''::text::timestamp;
ERROR: invalid input syntax for type timestamp: "'||_absd||'"
the below line:
(select date_part(''day'',((due_date::timestamp) - ('||_absd||' ::text::timestamp)) )as days_ago, item_amount, '||_absd||'::text::date as absd from fdw_test)
was changed slightly to this:
(select date_part(''day'',((due_date::timestamp) - ('''||_absd||'''::timestamp)) )as days_ago, item_amount, '''||_absd||'''::date as absd from fdw_test)
the function now works as expected

Postgresql function with values (from another table) as arguments

I can't figure out how to call a function with inputs specified from another table.
Let us assume the following function is being used to create a time interval:
create or replace function interval_generator(dt_start timestamp with TIME ZONE,
dt_end timestamp with TIME ZONE,
round_interval INTERVAL)
returns TABLE(time_start timestamp with TIME ZONE,
time_end timestamp with TIME ZONE) as $$
BEGIN
return query
SELECT
(n) time_start,
(n + round_interval) time_end
FROM generate_series(date_trunc('minute', dt_start), dt_end, round_interval) n;
END
$$
LANGUAGE 'plpgsql';
Let us create a dummy table for the minimal example:
DROP TABLE IF EXISTS lookup;
CREATE TEMP TABLE lookup
as
select *
from (
VALUES
('2017-08-17 04:00:00.000'::timestamp),
('2017-08-17 05:00:00.000'::timestamp),
('2017-08-18 06:00:00.000'::timestamp)
) as t (datetime);
Now my attempt is as follows:
select interval_generator(
SELECT datetime FROM lookup Order By datetime limit 1,
SELECT datetime FROM lookup Order By datetime Desc limit 1,
'1 hours'::interval
);
and it just yields the generic error ERROR: syntax error at or near "SELECT"
Enclose the SELECT statements in parentheses to make them expressions like this:
select * from interval_generator(
(SELECT datetime FROM lookup Order By datetime limit 1),
(SELECT datetime FROM lookup Order By datetime Desc limit 1),
'1 hours'::interval
);
Please note that
SELECT datetime FROM lookup Order By datetime limit 1
is exactly
SELECT min(datetime) FROM lookup
which seems to me better readable. As the function body of interval_generator comprises of a single SQL query why don't you make it a plain SQL function instead of pl/pgsql?
<your-function-declaration> as $$
SELECT
(n) time_start,
(n + round_interval) time_end
FROM generate_series(date_trunc('minute', dt_start), dt_end, round_interval) n;
$$
LANGUAGE 'sql';

Difficulties using Postgresql's EXTRACT tool via function call

I'm trying to figure out how to use the Postgresql EXTRACT function to convert a given date_variable into its equivalent day of the week. I understand that it will convert the date_variable into a numbering from 0 - 6 (0 is Sunday, 6 is Saturday etc)
I've created a simple table to test my queries. Here I will attempt to convert the start_date into its DOW equivalent.
DROP TABLE IF EXISTS test;
CREATE TABLE test(
start_date date PRIMARY KEY,
end_date date
);
INSERT INTO test (start_date, end_date) VALUES ('2021-03-31', '2021-03-31'); -- Today (wed), hence 3
INSERT INTO test (start_date, end_date) VALUES ('2021-03-30', '2021-03-30'); -- Yesterday (tues), hence 2
INSERT INTO test (start_date, end_date) VALUES ('2021-03-29', '2021-03-29'); -- Day before (mon), hence 1
If I were to run the query below
SELECT (EXTRACT(DOW FROM t.start_date)) AS day FROM test t;
It works fine, and returns the result as intended (returns a single column table with values (3, 2, 1) respectively.)
However, when I attempt to write a function to return the exact same query
CREATE OR REPLACE FUNCTION get_day()
RETURNS TABLE (day integer) AS $$
BEGIN
RETURN QUERY
SELECT (EXTRACT(DOW FROM t.start_date)) as day
FROM test t;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM get_day(); -- throws error "structure of query does not match function result type"
I get an error instead. I cant seem to find the issue and don't know what is causing it.
extract() returns a double precision value, but your function is declared to return an integer. You need to cast the value:
CREATE OR REPLACE FUNCTION get_day()
RETURNS TABLE (day integer) AS $$
BEGIN
RETURN QUERY
SELECT EXTRACT(DOW FROM t.start_date)::int as day
FROM test t;
END;
$$ LANGUAGE plpgsql;
But you don't really need PL/pgSQL for this, a language SQL function would also work.
CREATE OR REPLACE FUNCTION get_day()
RETURNS TABLE (day integer) AS $$
SELECT EXTRACT(DOW FROM t.start_date)::int as day
FROM test t;
$$
LANGUAGE sql
stable;
As you are not passing any parameters, I would actually use a view for this:
create or replace view day_view
AS
SELECT EXTRACT(DOW FROM t.start_date)::int as day
FROM test t;
The extract function returns values of type double precision.
You declare result to be integer.
You should cast the result of EXTRACT to integer:
CREATE OR REPLACE FUNCTION get_day()
RETURNS TABLE (day integer) AS $$
BEGIN
RETURN QUERY
SELECT EXTRACT(DOW FROM t.start_date)::integer as day
FROM test t;
END;
$$ LANGUAGE plpgsql;

Data arguments passing to user-defined function postgresql

I'm trying to create user-defined function which will return SUM from expenses view based on given dates.
CREATE FUNCTION budget.getTotalAmountFromView (startDate DATE, endDate DATE)
RETURNS DECIMAL AS $$
DECLARE
totalValue DECIMAL := 0;
BEGIN
SELECT INTO totalValue sum(amount) from budget.epenses_overview where transaction_date >= startDate AND transaction_date <= endDate;
RETURN totalValue;
END;
$$ LANGUAGE plpgsql;
I am trying to invoke it using:
SELECT * FROM budget.getTotalAmountFromView(TO_DATE(20190201, YYYYMMDD), TO_DATE(20190225, YYYYMMDD));
But it returns error
AFTER CHANGES:
Function shall be assigned to the right schema -> budget;
and invoke:
SELECT budget.getTotalAmountFromView('20190201'::DATE, '20190225'::DATE);
You don't need FROM for scalar function:
SELECT budget.getTotalAmountFromView22('20190201'::DATE, '20190225'::DATE);
You are lacking single quotes around the date and format strings.
TO_DATE(20190201, YYYYMMDD) should be TO_DATE('20190201', 'YYYYMMDD')

How to do postgresql select query funciton using parameter?

I want to create a postgresql funciton that returns records. But if I pass an id parameter, it should be add in where clause. if I do not pass or null id parameter, where clasuse will not add the query.
CREATE OR REPLACE FUNCTION my_func(id integer)
RETURNS TABLE (type varchar, total bigint) AS $$
DECLARE where_clause VARCHAR(200);
BEGIN
IF id IS NOT NULL THEN
where_clause = ' group_id= ' || id;
END IF ;
RETURN QUERY SELECT
type,
count(*) AS total
FROM
table1
WHERE
where_clause ???
GROUP BY
type
ORDER BY
type;
END
$$
LANGUAGE plpgsql;
You can either use one condition that takes care of both situations (then you don't need PL/pgSQL to begin with):
CREATE OR REPLACE FUNCTION my_func(p_id integer)
RETURNS TABLE (type varchar, total bigint)
AS $$
SELECT type,
count(*) AS total
FROM table1
WHERE p_id is null or group_id = p_id
GROUP BY type
ORDER BY type;
$$
LANGUAGE sql;
But an OR condition like that is typically not really good for performance. The second option you have, is to simply run two different statements:
CREATE OR REPLACE FUNCTION my_func(p_id integer)
RETURNS TABLE (type varchar, total bigint)
AS $$
begin
if (p_id is null) then
return query
SELECT type,
count(*) AS total
FROM table1
GROUP BY type
ORDER BY type;
else
return query
SELECT type,
count(*) AS total
FROM table1
WHERE group_id = p_id
GROUP BY type
ORDER BY type;
end if;
END
$$
LANGUAGE plgpsql;
And finally you can build a dynamic SQL string depending the parameter:
CREATE OR REPLACE FUNCTION my_func(p_id integer)
RETURNS TABLE (type varchar, total bigint)
AS $$
declare
l_sql text;
begin
l_sql := 'SELECT type, count(*) AS total FROM table1 '
if (p_id is not null) then
l_sql := l_sql || ' WHERE group_id = '||p_id;
end if;
l_sql := l_sql || ' GROUP BY type ORDER BY type';
return query execute l_sql;
end;
$$
LANGUAGE plpgsql;
Nothing is required just to use the variable as it is for more info please refer :plpgsql function parameters