If I want to add 5 days to a date, I can do it using the INTERVAL function:
select create_ts + interval '5 days' from abc_company;
However, my table has a field called num_of_days and I want to add it to my create_ts. Something like this:
select create_ts + interval num_of_days || ' days' from abc_company;
This does not work. How can I accomplish this in postgresql?
Simply multiply the value with an interval:
select create_ts + num_of_day * interval '1' day
from abc_company;
Since Postgres 9.4 this is easier done using the make_interval() function:
select create_ts + make_interval(days => num_of_day)
from abc_company;
You just need a working type cast. This kind is standard SQL.
select current_timestamp + cast((num_of_days || ' days') as interval)
from abc_company;
This is an alternative syntax, peculiar to PostgreSQL.
select current_timestamp + (num_of_days || ' days')::interval
from abc_company;
I prefer not trying to remember the third kind of type cast supported by PostgreSQL, which is the function-like syntax.
select current_timestamp + "interval" (num_of_days || ' days')
from abc_company;
Why? Because some function names have to be quoted; interval is one of them.
Also, the names interval, time, and timestamp can only be used in this
fashion if they are double-quoted, because of syntactic conflicts.
Therefore, the use of the function-like cast syntax leads to
inconsistencies and should probably be avoided.
here is a function that I use:
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';
WEEK_CONST Char(15) := 'week';
DAY_CONST Char(15) := 'day';
HOUR_CONST Char(15) := 'hour';
dateTemp timestamp;
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(WEEK_CONST) THEN
select cast(cast(incrementvalue as character varying) || ' week' as interval) into intervals;
ELSEIF lower($1) = lower(HOUR_CONST) THEN
select cast(cast(incrementvalue as character varying) || ' hour' as interval) into intervals;
END IF;
dateTemp := inputdate + intervals;
RETURN dateTemp;
END;
$$ LANGUAGE plpgsql;
Used like so:
select dateadd('day', 3, current_timestamp);
It supports adding years, months, weeks, days, hours. More support could be added
Related
I am reviewing the functions but I cannot solve this one;
Write a function that returns every Friday 13th during a specific year.
Example:
SELECT * FROM martes13(2020);
13/01/2020
13/03/2020
13/08/2020
My unfortunate attempt, do not pay much attention.
DECLARE
diaInicial date;
diaFinal date;
anio1 date;
anio2 date;
auxData date;
dates date[];
BEGIN
diaInicial := ('01/' || '01/' || anio ) :: date;
diaFinal := diaInicial + '1 YEAR' :: interval;
anio2:= date_part('year',diaFinal);
FOR i IN 1..12 BY 1 LOOP
FOR j IN 1..30 BY 1 LOOP
diaInicial := anio || '-' || i || '-' || j;
if(date_part('dom',auxData)==13 and date_part('dow',auxData)==5)then
dates[j] := diaInicial;
end if;
end loop;
end loop;
return dates;
END;
There's no way to solve it no matter how hard I try, I understand that I have to use dates, years intervals and counters but it does not work out. Any help or information could be of use to me.
Thanks in advance.
That can be solved with a simple SQL statement:
SELECT CAST(d AS date)
FROM generate_series(
TIMESTAMP '2020-01-13',
TIMESTAMP '2020-12-13',
INTERVAL '1 month'
) AS thirteen(d)
WHERE EXTRACT (dow FROM d) = 5;
You could wrap that in an SQL function.
My story evaluated from this:
I created a function create dynamic partition table with Table_Year_Month format such as table_2018_04, table_2018_05 .... The arguments of creation partition function are bigint such as create_partition_function(1518164237,1520583437) ;. After that I convert bigint to date to can get year and month from timestamp. But the check function ( check(timestamp >= date)) don't work
I can't compare bigint >= date in sql. What's operator can compare their?
I tried convert timestamp to datetime with UNIX_TIMESTAMP function of sql but don't work
CREATE OR REPLACE FUNCTION create_partition_function( DATE, DATE )
returns void AS $$
DECLARE
create_query text;
index_query text;
BEGIN
FOR create_query, index_query IN SELECT
'create table user_event_firebase_'
|| TO_CHAR( d, 'YYYY_MM' )
|| ' ( check( timestamp >= UNIX_TIMESTAMP(date,'%Y %M %D' )'''
|| TO_CHAR( d, 'YYYY-MM-DD' )
|| ''' and timestamp < UNIX_TIMESTAMP(date,'%Y %M %D' ) '''
|| TO_CHAR( d + INTERVAL '1 month', 'YYYY-MM-DD' )
|| ''' ) ) inherits ( user_event_firebase );',
'create index user_event_firebase_'
|| TO_CHAR( d, 'YYYY_MM' )
|| '_time on user_event_firebase_'
|| TO_CHAR( d, 'YYYY_MM' )
|| ' ( timestamp );'
FROM generate_series( to_timestamp($1), to_timestamp($2), '1 month'::interval ) AS d
LOOP
EXECUTE create_query;
EXECUTE index_query;
END LOOP;
END;
$$
language plpgsql;
p/s : bigint and dateare data type in sql.
ERROR: invalid input syntax for integer: "2018-02-09"
CONTEXT: SQL statement "create table user_event_firebase_2018_02 ( check( timestamp >= bigint '2018-02-09' and timestamp < bigint '2018-03-09' ) ) inherits ( user_event_firebase );"
PL/pgSQL function create_partition_function(date,date) line 21 at EXECUTE
You can convert date/timestamp to seconds using
SELECT extract(epoch from '2016-05-03'::date)
--result: 1462233600
SELECT to_timestamp(1462233600)::date;
--result: '2016-05-03'
I'm trying to make a function on psql. It will be triggered on insert on table. I want to inject an variable on my select. Can't get working ...
CREATE OR REPLACE FUNCTION updateHistoricLongTime()
RETURNS void AS $$
DECLARE
hour_nb int;
index_hour int;
saved_hours int;
tmp_counter int;
BEGIN
hour_nb := 0;
index_hour := 1;
saved_hours := 2160;
tmp_counter := 0;
SELECT COUNT(*) FROM locationhistoric WHERE type='hour' AND idLocation=6 INTO hour_nb;
IF (hour_nb<saved_hours) THEN
FOR i IN 1 .. saved_hours LOOP
SELECT COUNT(*) FROM visits
WHERE stend < (timestamp '2017-11-29 15:00' - interval **>> index_hour<<<** - 1 ' hour') AND stend > (timestamp '017-11-29 15:00' - interval **>>index_hour <<<**' hour') AND location_id=6 AND duration>0 INTO tmp_counter;
index_hour := index_hour + 1;
END LOOP;
END IF;
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE;
How can I inject variable index_hour in my SELECT COUNT(*) FROM Visits ...
EDIT: It's just syntax issue, but I can't manage to find the right way !
The result in command line:
ERROR: syntax error at or near "index_hour"
LINE 16: ... stend < (timestamp '2017-11-29 15:00' - interval index_hour...
Thanks a lot,
The solution
CREATE OR REPLACE FUNCTION updateHistoricLongTime()
RETURNS void AS $$
DECLARE
hour_nb int;
index_hour int;
saved_hours int;
tmp_counter int;
index_hour_minor int;
BEGIN
hour_nb := 0;
index_hour := 1;
index_hour_minor := 0;
saved_hours := 2160;
SELECT COUNT(*)
INTO hour_nb
FROM locationhistoric
WHERE type='hour'
AND idLocation=6;
IF (hour_nb<saved_hours) THEN
FOR i IN 1 .. saved_hours LOOP
SELECT COUNT(*)
INTO tmp_counter
FROM visits
WHERE start > timestamp '2017-11-29 15:00' - ( interval '1 hour' * index_hour )
AND start < timestamp '2017-11-29 15:00' - ( interval '1 hour' * index_hour_minor)
AND location_id=6
AND duration>0;
INSERT INTO locationhistoric
(type, date, counter, idLocation)
VALUES( 'hour',
timestamp '2017-11-29 15:00' - ( interval '1 hour' * index_hour_minor),
tmp_counter,
6);
index_hour_minor := index_hour_minor + 1;
index_hour := index_hour + 1;
END LOOP;
END IF;
END;
$$
LANGUAGE plpgsql;
The value specified for an interval can't be passed as a variable. However if the base unit is always an hour you can multiply a one our interval with the desired number of ours, e.g.
interval '1' hour * 5
will return 5 hours. The 5 can be a parameter. So your query should be:
SELECT COUNT(*)
INTO tmp_counter
FROM visits
WHERE stend < (timestamp '2017-11-29 15:00' - (interval '1' hour * index_hour))
AND stend > (timestamp '2017-11-29 15:00' - (interval '1' hour * index_hour))
AND location_id=6
AND duration > 0;
The syntax you want to get for your query (where index_hour = 8, for example) is:
select count(*)
from visits
where
stend < (timestamp '2017-11-29 15:00' - interval '7 hour') and
stend > (timestamp '2017-11-29 15:00' - interval '8 hour') and
location_id = 6 and
duration > 0;
Note where the quotes are. This means that your variable has to be inside quotes in pl/pgsql and that means it will be treated as a literal.
The solution is:
execute
'select count(*) ' ||
'from visits ' ||
'where ' ||
'stend < (timestamp ''2017-11-29 15:00'' - interval ''' || (index_hour - 1) || ' hour'') and ' ||
'stend > (timestamp ''2017-11-29 15:00'' - interval ''' || index_hour || ' hour'') and ' ||
'location_id = 6 and ' ||
'duration > 0'
To save me setting up your data I've written a simpler example using a table that I have (driver) so that I could test. Note that you have to use 2 single quotes to get one single quote into a string and that means counting quotes carefully.
create function a47768241() returns integer
as $body$
declare
index_hour int;
id integer;
begin
index_hour = 8;
execute
'select id ' ||
'from driver ' ||
'where ' ||
'from_date_time < (timestamp ''2013-04-22 16:00:00'' - interval ''' || (index_hour - 1) || ' hour'') '
into id;
return id;
end;
$body$
language 'plpgsql';
Simple test:
# select a47768241();
a47768241
-----------
158
(1 row)
Using the result value to check the date:
# select * from driver where id = a47768241();
id | vehicle_id | person_id | from_date_time | to_date_time | created_at | updated_at
-----+------------+-----------+---------------------------+---------------------------+----------------------------+------------
158 | 6784 | 15430 | 2012-09-13 17:00:41.39778 | 2012-09-14 01:54:46.39778 | 2016-06-03 16:43:11.456063 |
(1 row)
just concat the interval value, like
interval concat(index_hour - 1 , ' hour')
im new in postgre and im trying to write stored procedure with the following code :
CREATE OR REPLACE FUNCTION public.get_month_status()
returns table (request_detail character varying, request_detail2 character
varying, request_detail3 character varying)
language plpgsql stable
as $function$
BEGIN
return query
select
CASE
WHEN (extract(DAY FROM now()) >= 25) THEN (extract(MONTH FROM now()) || '-' || extract(Year FROM now()))
ELSE (extract(MONTH FROM now()) - 1 || '-' || extract(Year FROM now()))
end,
CASE
WHEN (extract(month FROM now()) = 2) THEN (extract(month FROM now()) -1 || '-' || extract(Year FROM now()))
ELSE (extract(month FROM now()) || '-' || extract(Year FROM now()))
end,
CASE
WHEN (extract(month FROM now()) = 1) THEN (extract(month FROM now() - interval '2 months' ) || '-' || extract(Year FROM now()) -1)
ELSE (extract(month FROM now()) || '-' || extract(Year FROM now()))
end;
end;
$function$;
but whenever i tried to execute the commmand using select * from get_month_status() but i got an error saying
SQL Error [42804]: ERROR: structure of query does not match function result type
Detail: Returned type text does not match expected type character varying in column 1.
Where: PL/pgSQL function get_month_status() line 3 at RETURN QUERY
org.postgresql.util.PSQLException: ERROR: structure of query does not match
function result type
Detail: Returned type text does not match expected type character varying in column 1.
Where: PL/pgSQL function get_month_status() line 3 at RETURN QUERY
how do i make it work? someone help!
Error message says what is wrong:
Returned type text does not match expected type character varying
just change returned table's column types to text and recreate function
... returns table (request_detail text, request_detail2 text, request_detail3 text) ...
I currently do not know ANSI equivalent to Teradata FORMAT key word for converting timestamp, date data types into our required representation formats. I know this can be done with to_char, to_date like individual database specific functions, But I want to write in ANSI so that in future I can easily move code running from one DB to another. Below is the current Teradata SQL I am trying to convert into ANSI so that I can run it on both Teradata ,Netezza and Vertica etc.
SELECT
CAST( (MYTIME ( FORMAT 'DDMMYYYY')) AS CHAR( 8 ))
|| CAST( (MYTIME (FORMAT 'HHMISS')) AS CHAR(6))
|| CAST(CAST(MYNUMBER AS FORMAT'-9(5)') AS CHAR(5))
FROM MYTABLE
;
Currently I don't know how to translate the FORMAT 'HHMISS', FORMAT '-9(5)' into ANSI. Is there any documentation on this possible ANSI equivalent functions if any? Please help.
You can try the EXTRACT() function. YMMV, but most modern RDBMS's support it. There's really no good answer here - every DBMS handles "date formatting" issues differently and confusingly.
select
case when extract(day from current_timestamp) < 10 then '0' else '' end || cast(extract(day from current_timestamp) as varchar(2)) ||
case when extract(month from current_timestamp) < 10 then '0' else '' end || cast(extract(month from current_timestamp) as varchar(2)) ||
cast(extract(year from current_timestamp) as varchar(4)) ||
case when extract(hour from current_timestamp) < 10 then '0' else '' end || cast(extract(hour from current_timestamp) as varchar(2)) ||
case when extract(minute from current_timestamp) < 10 then '0' else '' end || cast(extract(minute from current_timestamp) as varchar(2)) ||
case when extract(second from current_timestamp) < 10 then '0' else '' end || cast(extract(second from current_timestamp) as varchar(2))
In Teradata you should be able to do the following:
/* The double cast is to truncate any time value associated with MyTime */
SELECT CAST(CAST(MyTime AS DATE) AS TIMESTAMP(6))
+ ((MyTime - TIME '00:00:00') HOUR to SECOND(6)) AS MyTimeStamp_
FROM MyTable;
Another option to try:
SELECT CreateTimeStamp
, CAST((CreateTimeStamp (TIMESTAMP(6), FORMAT 'DDMMYYYYBHHMISS.S(6)')) AS CHAR(22)) AS MyTimeStamp_
, CAST((CreateTimeStamp (TIMESTAMP(6), FORMAT 'DD-MM-YYYYBHH:MI:SS.S(6)')) AS CHAR(26)) AS MyTimeStamp2_
FROM DBC.Databases
WHERE DatabaseName = USER;
My experience has been that you have to treat dates and timestamps differently with each database. One solution may not be universally accepted. But I'd love to see something that works in Teradata, Oracle, SQL Server, MySQL, etc.