PGSQL: Adding a variable interval - Syntax error - postgresql

I have a quick question. I am fairly new to pgsql and I am unable to figure out how to fix the syntax error below.
Here is what I am trying to do
start_date := '2011-01-01'::date;
end_date := '2011-03-01'::date;
duration := '6 months'
while start_date < end_date loop
window_start_date = start_date;
window_end_date = window_start_date + interval||duration||;
end loop;
However I keep getting a syntax error.
ERROR: column "interval" does not exist
LINE 1: SELECT $1 + interval|| $2 ||
^
QUERY: SELECT $1 + interval|| $2 ||
What am I doing wrong. Any help would be much appreciated

Guesswork (the rest of the function definition is missing).
This would work in PL/pgSQL (which are using behind the curtains):
window_end_date := window_start_date + interval duration;
Or:
window_end_date := window_start_date + duration::interval;
Cast the text value to interval to make it work. But it would be better to declare the variable duration as interval to begin with (maybe that is the case, then drop the cast - information missing).
The assignment operator in plpgsql is :=, not =.
The result is a timestamp, not a date. But it will be coerced to date in your example.

Related

PostgreSQL - Reference a variable value in select statement interval

I'm trying to calculate a value in a variable then use that value to drive a interval statement further on.
Running on PG 9.6.
We can get to PG 13 if there is something added since then we can use.
Example:
CREATE OR REPLACE FUNCTION public.demofunc(
int,
int,
date)
returns date
LANGUAGE plpgsql AS $$
DECLARE
diffvalue text;
returndate date;
BEGIN
diffvalue:= ($2 - $1);
returndate := (SELECT $3 - INTERVAL 'diffvalue days');
return returndate;
END$$;
This is a simplified version of what I want to achieve, the interval of the numbers of days to remove is based on a calculation that's done in the function. So its not a simple A - B but the end result is
I can't seem to get the function to resolve the "diffvalue" before running the select statement. I've tried using int and text and varchar and concat the string for anything.
Sorry for anything obvious i might be missing, only started this today.
If your diffvalue is in reality an integer, you can use the make_interval function to create an interval based on that number:
returndate := $3 + make_interval(days => diffvalue);
If diffvalue is a decimal that can represent fractional days, make_interval can't be used.
In that case you can multiply an interval of 1 day with that value:
returndate := $3 + interval '1 day' * diffvalue;

PostgreSQL - using variable inside quotes

I am trying to use a variable for an expression like this
NOW() - INTERVAL '5 days'
But getting errors:
CREATE OR REPLACE FUNCTION some.archive() RETURNS VOID AS
$$
DECLARE
p_archive_depth CONSTANT VARCHAR := '5 days';
BEGIN
IF (date(p_table_date) < date(NOW() - INTERVAL p_archive_depth))
THEN
RAISE INFO '%', p_table_name;
END IF;
END;
$$ LANGUAGE plpgsql;
Also tried without success:
'' || p_archive_depth || ''
'' p_archive_depth ''
You need to define the variable with the data type interval
DECLARE
p_archive_depth CONSTANT interval := interval '5 days';
BEGIN
IF date(p_table_date) < (now() - p_archive_depth)::date
a_horse_with_no_name's answer is, of course, correct, but I'd like to offer an alternative to round it out in case you're getting the string value dynamically (e.g., querying it, getting it as an argument from the user, etc). You could take the string value of '5 DAYS' and explicitly cast it to an interval using the :: operator:
IF (date(p_table_date) < date(NOW() - p_archive_depth::INTERVAL))
-- Here -----------------------------------------^

add interval to timestamp on record creation trigger

I am trying to do what on the surface seems a simple task. I have a table with the fields expires_at (timestamp) ttl (integer) and name (text)
the ttl is supplied as 86400. I want to set the expires_at field to be the current timestamp + the ttl.
this is my trigger
CREATE FUNCTION public.set_expires() RETURNS trigger
LANGUAGE plpgsql
AS $_$
BEGIN
NEW.expires_at := CURRENT_TIMESTAMP + (NEW.ttl || ' seconds')::INTERVAL;
RETURN NEW;
END;
$_$;
however, when this runs, I get the current time in the expires_at field.
if I change the trigger to be
NEW.expires_at := CURRENT_TIMESTAMP + (86400 || ' seconds')::INTERVAL;
then the expires_at is set correctly.
This would imply that NEW.ttl is not set, but if I add
NEW.name:=NEW.ttl;
to the trigger, name is set to 86400. So I am somewhat confused about what I am doing wrong here and would appreciate some help ;)
Althought your version should work (as #NickBarnes shows in the fiddle), maybe you could try to change the syntax to
NEW.expires_at := CURRENT_TIMESTAMP + NEW.ttl * interval '1 second';

SQL state: 22P02 invalid input syntax for integer error in recursive PL/pgSQL function

I am new to PostgreSQL and I am currently in the process of writing a recursive function to find tram times.
I have defined a custom type as:
CREATE TYPE single_journey AS
(tram_id integer,
departure_station text,
departure_time time without time zone,
destination_station text,
arrival_time time without time zone);
and the function...
CREATE OR REPLACE FUNCTION find_tram_same_line(text, text, time) returns single_journey AS $$
DECLARE
departure_station ALIAS FOR $1;
destination_station ALIAS FOR $2;
query_time ALIAS FOR $3;
journey single_journey;
BEGIN
journey.departure_station := departure_station;
journey.destination_station := destination_station;
SELECT tram_id, time
INTO journey.tram_id, journey.departure_time
FROM station_departure_times
JOIN stations on station_departure_times.station_id = stations.station_id
WHERE stations.name = departure_station
AND time > query_time
ORDER BY time ASC
LIMIT 1;
SELECT time
INTO journey.arrival_time
FROM station_departure_times
JOIN stations on station_departure_times.station_id = stations.station_id
WHERE stations.name = destination_station
AND tram_id = journey.tram_id;
IF journey.arrival_time IS NULL THEN
SELECT find_tram_same_line(
departure_station,
destination_station,
(query_time + interval '1 minute'))
INTO journey;
END IF;
RETURN journey;
END;
$$ LANGUAGE plpgsql;
The query:
SELECT find_tram_same_line('GrimesDyke', 'CitySquare', '09:00:00');
Whenever I run the query, I get an error:
********** Error **********
ERROR: invalid input syntax for integer: "(24,GrimesDyke,09:07:00,CitySquare,10:19:00)"
SQL state: 22P02
Context: PL/pgSQL function find_tram_same_line(text,text,time without time zone) line 29 at SQL statement
I have spent some time trying to figure out why this is to no avail. The only integer in the single_journey type is the tram_id but I am unsure why this is causing an issue. Does anyone know why this might be?
Edit: Should be noted that (24,GrimesDyke,09:07:00,CitySquare,10:19:00) is what I was expecting.

PL/PgSQL: No function matches the given name and argument types. You might need to add explicit type casts

I am trying to write a dateadd() function using PL/PgSQL. I want to be able to add anything from seconds, right up to years to a date/timestamp. have cobbled together a function (from snippets etc obtained online), and have come up with this "implementation":
CREATE OR REPLACE FUNCTION dateadd(diffType VARCHAR(15), incrementValue int, inputDate timestamp) RETURNS timestamp AS $$
DECLARE
YEAR_CONST Char(15) := 'year';
MONTH_CONST Char(15) := 'month';
DAY_CONST Char(15) := 'day';
HOUR_CONST Char(15) := 'hour';
MIN_CONST Char(15) := 'min';
SEC_CONST Char(15) := 'sec';
dateTemp Date;
intervals interval;
BEGIN
IF lower($1) = lower(YEAR_CONST) THEN
select cast(cast(incrementvalue as character varying) || ' year' as interval) into intervals;
ELSEIF lower($1) = lower(MONTH_CONST) THEN
select cast(cast(incrementvalue as character varying) || ' months' as interval) into intervals;
ELSEIF lower($1) = lower(DAY_CONST) THEN
select cast(cast(incrementvalue as character varying) || ' day' as interval) into intervals;
ELSEIF lower($1) = lower(HOUR_CONST) THEN
select cast(cast(incrementvalue as character varying) || ' hour' as interval) into intervals;
ELSEIF lower($1) = lower(MIN_CONST) THEN
select cast(cast(incrementvalue as character varying) || ' minute' as interval) into intervals;
ELSEIF lower($1) = lower(SEC_CONST) THEN
select cast(cast(incrementvalue as character varying) || ' second' as interval) into intervals;
END IF;
dateTemp:= inputdate + intervals;
RETURN dateTemp;
END;
$$ IMMUTABLE LANGUAGE plpgsql;
However, when I try to use the function, I get the following error:
template1=# select dateadd('hour', 1, getdate());
ERROR: function dateadd(unknown, integer, timestamp with time zone) does not exist
LINE 1: select dateadd('hour', 1, getdate());
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
template1=#
Why is the function not being found by PG?
I am running PG 9.3 on Ubuntu 12.0.4 LTS
You'll kick yourself: the difference between the line that's erroring (and the next few) and the one before it is that you've accidentally added an underscore in the wrong place:
You have
HOUR_CONST_Char(15) := 'hour';
It should be
HOUR_CONST Char(15) := 'hour';
EDIT
The updated question is suffering from Postgres's slightly fussy type system: your getdate() function is returning timestamp with time zone, but your dateadd accepts a timestamp (i.e. timestamp without time zone). Using the Postgres short-hand for cast of value::type, this should work (SQLFiddle demo, using now() in place of getdate())
select dateadd('hour', 1, getdate()::timestamp);
However, you have a few other odd type selections:
Your "constants" are Char(15), but aren't 15 characters long, so will be padded with spaces; you should probably use VarChar(15), or even just text (unlike MS SQL, in Postgres, all strings are stored out-of-page dynamically, and a VarChar is essentially just text with a maximum length constraint).
Your intermediate variable (which can probably be refactored out) is of type Date, not Timestamp, so will truncate the input to just the date part, no time.
Finally, I'll note one fundamental optimisation: you don't need to lower() your constants, because you already know they're lower case. :)
It's the underscore between HOUR_CONST and Char(15) You should enter "HOUR_CONST Char(15)" instead of "HOUR_CONST_Char(15)"