Data arguments passing to user-defined function postgresql - 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')

Related

passing variable date with fixed time to a function

I am trying to create a function which accepts two arrays, and a date. The function uses the date
in a way where I want hardcoded values of time (with timezone) which are already stated in the function body (in the orig_dataset CTE). Here is my function so far:
CREATE or replace FUNCTION f_loop_in_lockstep_final(_id_arr int[], _counter_arr int[], d_date date)
RETURNS TABLE (uc_name_ varchar)
LANGUAGE plpgsql AS
$func$
DECLARE
_id int;
_counter int;
d_date date;
BEGIN
FOR _id, _counter IN
SELECT *
FROM unnest (_id_arr, _counter_arr) t
LOOP
RETURN QUERY
with orig_dataset as (
select routes
from campaign_routes cr
where cr.created_at between 'd_date 06:00:00 +05:00' and 'd_date 18:00:00 +05:00'
)
-- a couple of further CTE's result in a final CTE called final_cte
select * from final_cte;
END LOOP;
END
$func$;
When I use the following function call:
SELECT * FROM f_loop_in_lockstep_final('{454,454}'::int[]
, '{2,3}'::int[], to_date('2023-01-17','YYYY-MM-DD'));
I receive the following error:
SQL Error [22007]: ERROR: invalid input syntax for type timestamp with time zone: "d_date 06:00:00 +05:00"
Where: PL/pgSQL function f_loop_in_lockstep_final(integer[],integer[],date) line 14 at RETURN QUERY
Well, obviously 'd_date 06:00:00 +05:00' is not a valid date literal.
You need to add a time value to the variable to create a timestamp value based on that:
where cr.created_at between d_date + '06:00:00 +05:00'::time
and d_date + '18:00:00 +05:00'::time
I am not entirely sure that using a time zone offset in a time constant works correctly, so maybe you need:
where cr.created_at between ((d_date + '06:00:00'::time) at time zone '+05:00')
and ((d_date + '18:00:00'::time) at time zone '+05:00')

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;

Date part in WHERE clause of a function

I want to select persons from a table where the date is within a given month.
This is what I have so far, but it's not working:
CREATE OR REPLACE FUNCTION u7()
RETURNS character varying AS
$BODY$
DECLARE
data varchar=`data`;
mes varchar=`2016-11-21`;
incidencia varchar=`expulsions`;
valor varchar;
BEGIN
EXECUTE `SELECT `
||quote_ident(data)
||`FROM `
||quote_ident(incidencia)
||` WHERE data IN(select date_part(`month`, TIMESTAMP $1))`
INTO valor USING mes;
return valor;
END;
$BODY$
LANGUAGE plpgsql;
select * FROM u7();
Clean syntax for what you are trying to do could look like this:
CREATE OR REPLACE FUNCTION u7()
RETURNS TABLE (valor text) AS
$func$
DECLARE
data text := 'data'; -- the first 3 would typically be function parameters
incidencia text := 'expulsions';
mes timestamp = '2016-11-21';
mes0 timestamp := date_trunc('month', mes);
mes1 timestamp := (mes0 + interval '1 month');
BEGIN
RETURN QUERY EXECUTE format(
'SELECT %I
FROM %I
WHERE datetime_column_name >= $1
AND datetime_column_name < $2'
, data, incidencia)
USING mes0, mes1;
END
$func$ LANGUAGE plpgsql;
SELECT * FROM u7();
Obviously, data cannot be a text column and a timestamp or date column at the same time. I use datetime_column_name for the timestamp column - assuming it's data type timestamp.
Aside from various syntax errors, do not use the construct with date_part(). This way you would have to process every row of the table and could not use an index on datetime_column_name - which my proposed alternative can.
See related answers for explanation:
EXECUTE...INTO...USING statement in PL/pgSQL can't execute into a record?
Table name as a PostgreSQL function parameter
How do I match an entire day to a datetime field?

How to return results from a loop in plpgsql?

I want to see the result displaying dates like this
"2001-01-01 00:00:00+05:30"
"2001-01-02 00:00:00+05:30"
"2001-01-03 00:00:00+05:30"
"2001-01-04 00:00:00+05:30"
"2001-01-05 00:00:00+05:30"
"2001-01-06 00:00:00+05:30"
"2001-01-07 00:00:00+05:30"
so on...
but iam getting 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 insert_date_dimension(date) line 12 at SQL statement
can you tell me what is the issue
function i created
create or replace function insert_date_dimension("Date" date)
returns text as
$$
Declare dat date;
start_date date;
end_date date;
Begin
start_date:='2016/01/01';
end_date:='2016/12/31';
while start_date<=end_date
loop
select start_date;
start_date:=start_date+ interval '1 day';
End loop;
return start_date;
end;
$$
LANGUAGE 'plpgsql';
can you tell me what is the issue with this function i was not able to execute it.I want to take all the column in select statment..please tell me create or replace function insert_date_dimension("date" date)
returns setof date as $$
declare
dat date;
start_date date;
end_date date;
begin
start_date := '2016/01/01';
end_date := '2016/12/31';
while start_date <= end_date loop
--return next start_date;
select start_date,date_part('week',start_date),date_part('quarter',start_date),to_char(start_date, 'day'),to_char(start_date, 'month'),
extract(year from current_date),extract(month from current_date);
start_date:= start_date + interval '1 day';
end loop;
end;
$$ language plpgsql;
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 insert_date_dimension(date) line 11 at SQL statement
********** Error **********
No need for a user defined function. Just use generate_series:
select generate_series('2016/01/01'::date, '2016/12/31', '1 day');
But if you are just fiddling with plpgsql then return next a setof date
create or replace function insert_date_dimension("date" date)
returns setof date as $$
declare
dat date;
start_date date;
end_date date;
begin
start_date := '2016/01/01';
end_date := '2016/12/31';
while start_date <= end_date loop
return next start_date;
start_date := start_date + interval '1 day';
end loop;
end;
$$ language plpgsql;
plpgsql is an identifier. Do not quote it.