Bruce Wayne
Bruce Wayne

Reputation: 491

I am trying to convert a postgresql query to a plr function

I have a postgres query, which works fine when I run it as a query. However, I would like to convert it to pl/r and be able to dynamically input the start and end date.

The SQL that works is:

with date as (
select d as first_day,
d + interval '1 month' - interval '1 day' as last_day
from generate_series('2010-01-01'::date,
                     '2018-12-01'::date,
                     '1 month') as d
) select last_day::date as snapshot_date from date;

Would like to make a pl/r like:

DROP FUNCTION IF EXISTS standard.seq_monthly(min_date_str char, max_date_str char);
CREATE FUNCTION standard.seq_monthly(min_date_str char, max_date_str char)
RETURNS setof dates AS
$$ 
with date as (
select d as first_day,
d + interval '1 month' - interval '1 day' as last_day
from generate_series(min_date_str::date,
                     max_date_str::date,
                     '1 month') as d
) select last_day::date as snapshot_date from date;

$$
LANGUAGE 'plr';
select * from standard.seq_monthly('2010-01-01' , '2018-12-01')

However, I am getting error while running the function. The error is

R parse error caught in "PLR711818 <- function(min_date_str,max_date_str)

Have tried declaring the max min date to be date as well.

Any help is highly appreciated.

Upvotes: 3

Views: 199

Answers (2)

Parfait
Parfait

Reputation: 107687

PL/R is a procedural language extension in PostgreSQL (similar to plpython, plperl, plphp) where valid, compliant R language code can be run. You are attempting SQL which cannot by itself run inside an R session, so your code will fail within a PG plr stored function.

However, there is no need for such an extension as your needs can be handled with the very basic SQL language (often more times efficient) to return needed date range table by specified input range:

CREATE OR REPLACE FUNCTION seq_monthly(min_date_str char, max_date_str char)
RETURNS TABLE(snapshot_date date) AS
$$ 
     with mydate as (
          select d as first_day,
                 DATE_TRUNC('month', d) 
                     + '1 MONTH'::INTERVAL 
                     - '1 DAY'::INTERVAL as last_day
          from generate_series(min_date_str::date,
                               max_date_str::date,
                               '1 month') as d
     ) 

     select last_day::date as snapshot_date from mydate;

$$
LANGUAGE SQL STABLE;

select * from seq_monthly('2010-01-31' , '2018-12-31');

Rextester demo


Now, if your really want a plr stored function, use R's seq() on given date range:

CREATE FUNCTION standard.seq_monthly(min_date_str char, max_date_str char)
RETURNS setof dates AS
$$ 
    seq(as.Date(min_date_str), as.Date(max_date_str), by='month')
$$
LANGUAGE 'plr';

select * from standard.seq_monthly('2010-01-01' , '2018-12-01')

Upvotes: 2

Bruce Wayne
Bruce Wayne

Reputation: 491

One of the solutions is to not go via plr, but write a sql query:

with max_min_date as(
select max(snapshot_date) as max_date_str, min(snapshot_date) as min_date_str from data
) , 
ts as (
select d as first_day,
d + interval '1 month' - interval '1 day' as last_day
from generate_series((select min_date_str from max_min_date)::date,
                     (select max_date_str from max_min_date)::date,
                     '1 month') as d
) select last_day::date as snapshot_date from ts;

In case, you'd need end of month date instead of start of month:

with max_min_date as(
  with max_min_wrk as (
    select max(snapshot_date) as max_date_str, min(snapshot_date) as min_date_str from data
    ) select cast(date_trunc('month', max_date_str) as date) as max_date, cast(date_trunc('month', min_date_str) as date) as min_date from max_min_wrk
), 
ts as (
select d as first_day,
d + interval '1 month' - interval '1 day' as last_day
from generate_series((select min_date from max_min_date)::date,
                     (select max_date from max_min_date)::date,
                     '1 month') as d
) select last_day::date as snapshot_date from ts 
order by snapshot_date asc;

Upvotes: 1

Related Questions