PostgreSQL now() value don't change value in function - postgresql

I need measure timelaps for testing the duration of function...
1) at the begin of function I assign value of now() to a variable called v_start;
2) before end the function I assign value of now() to a variable called v_end;
the problem is: the value of now() don't change during execution of function
Example
...
begin
v_start := now(); ex. "2018-02-14 10:03:52.394263+01"
...
...
v_end := now(); -- this value is equal to v_start
select EXTRACT(EPOCH FROM now()-now())/3600 into v_timelaps;
-- v_timelaps is always 0;
end;
Any solutions??? Thanx!

It is not supposed to. Because function runs in transaction and transaction start time does not change while function runs.
https://www.postgresql.org/docs/current/static/functions-datetime.html
clock_timestamp() timestamp with time zone Current date and time
(changes during statement execution);
or
statement_timestamp() timestamp with time zone Current date and
time (start of current statement);
not the
now() timestamp with time zone Current date and time (start of
current transaction);
formatting mine.
also check the https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT

Related

How can i create n columns in postgresql?

I am trying to write a function that returns random start time (it must be between now and a week long) and an endtime. I wrote this function:
CREATE OR REPLACE FUNCTION random_start_end_time(n integer)
RETURNS TABLE (startime TIMESTAMP WITH TIME ZONE, endtime TIMESTAMP WITH TIME ZONE)
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
RETURN QUERY
SELECT NOW() + (random() * (NOW()+'7 days' - NOW())) as startime;
SELECT NOW() + (random() * (NOW()+'7 days' - NOW())) as endtime;
END;
$BODY$
I can't find out how to generate multiple columns. For example I want n=100 columns of random start time and end time to be generated.
In general I can't understand how I can fill an empty table (with this function I am going to fill a table later).
Any thoughts would be valuable.
Thank you.
Use RETURN NEXT to add a row to the result set of a table function and RETURN to end the function execution. You also have to decide if you want a function that returns two columns or two rows. Your case looks like you want to do something like:
CREATE OR REPLACE FUNCTION random_start_end_time(
OUT starttime TIMESTAMP WITH TIME ZONE,
OUT endtime TIMESTAMP WITH TIME ZONE
) RETURNS record
LANGUAGE sql AS
$BODY$
WITH start AS (
SELECT current_timestamp + random() * INTERVAL '7 days' as starttime
)
SELECT starttime,
starttime + random() * INTERVAL '7 days' as endtime
FROM start;
$BODY$;
Call it like
SELECT * FROM random_start_end_time();
If you really want to return several rows, that would be
CREATE OR REPLACE FUNCTION random_start_end_time()
RETURNS SETOF timestamp with time zone
LANGUAGE plpgsql AS
$BODY$
BEGIN
RETURN NEXT current_timestamp + random() * INTERVAL '7 days';
RETURN NEXT current_timestamp + random() * INTERVAL '7 days';
RETURN; /* end the function */
END;
$BODY$;

How to extract miliseconds from timestamp dynamically in postgres plpgsql?

I want to get milliseconds from a 'Timestamp with timezone' using a plsql function.
I am able to generate the following function, but it is leading to truncation of miliseconds.
CREATE or REPLACE FUNCTION getMSFromTime(t1 timestamp with time zone)
RETURNS bigint AS $$
declare time1 double precision ;
BEGIN
SELECT EXTRACT(EPOCH FROM t1) into time1;
return time1;
END;
$$ LANGUAGE plpgsql;
but it ignores miliseconds
SELECT getMSFromTime('2019-02-11 08:01:33.423+00') //1549872093
SELECT getMSFromTime('2019-02-11 08:01:33.000+00') //1549872093
I am able to get a PostgreSQL way so that millisecond decimals are preserved as well, using:
SELECT EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE '2019-02-11 08:01:33.423+00'); // 1549872093.423
But I am not able to integrate it into a function and it gives following error:
syntax error at or near "t1"
LINE 5: ...ELECT EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE t1) into t..
CREATE or REPLACE FUNCTION getMSFromTime2(t1 timestamp with time zone)
RETURNS bigint AS $$
declare time1 double precision ;
BEGIN
SELECT EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE t1) into time1;
return time1;
END;
$$ LANGUAGE plpgsql;
Please suggest a way so as to create a PostgreSQL function which can do this functionality.
extract() returns a double value that represents seconds, by casting that to a bigint you lose the fractional seconds (=milliseconds)
If you want a bigint representing milliseconds, you need to multiple the result with 1000.
There is also no reason to use PL/pgSQL for such a simple thing:
CREATE or REPLACE FUNCTION getmsfromtime(t1 timestamp with time zone)
RETURNS bigint
AS $$
SELECT (EXTRACT(EPOCH FROM t1)*1000)::bigint;
$$
LANGUAGE sql;

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';

POSTGRESQL - calling function with different times as input parameter

I was trying to call the function I created to fetch some data for particular time slot. Given below is a script of my function:
CREATE OR REPLACE FUNCTION my_function(
starttime timestamp with time zone,
endtime timestamp with time zone)
RETURNS TABLE("Deviceid" integer, "AlertTime" timestamp with time zone)
LANGUAGE 'plpgsql'
COST 100.0
AS $function$
DECLARE
r record;
BEGIN
SELECT "DeviceID" , "AlertTime" FROM my_table
WHERE "AlertTime" BETWEEN starttime AND endtime;
END;
$function$;
ALTER FUNCTION public.my_function(timestamp with time zone, timestamp with time zone)
OWNER TO postgres;
When I am calling function with time '2016-12-15 00:00:01' to '2016-12-15 18:00:00' I am not getting any record. Even many records available for the time slot, I checked it by passing same time values for the query inside the function, its fetching data properly.
select * from my_function('2016-12-14 00:00:01','2016-12-15 18:00:00')
But when I am calling function with 2016-12-15 00:00:00' to '2016-12-15 18:00:00' I am able to get all records.
select * from my_function('2016-12-14 00:00:00','2016-12-15 18:00:00')
Even I tried to change the input parameters to "character varying" and convert the input internally to time stamp even then it is not working.
The explanation is obvious, isn't it?
All the matching rows from mytable have "AlertTime" greater or equal than 2016-12-14 00:00:00 and less than 2016-12-14 00:00:01.

postgresql date_trunc to arbitrary precision?

postgresql has date_trunc that can truncate the time stamp value to a specific unit, like hour or minute. I want to know if there's any build-in function that would allow me to truncate to 10 minutes?
I know one trick is to convert the time stamp to epoch, do some math, then convert back. But I don't like it.
There is no function you want, but as said in postgresql wiki you can define function for youself:
CREATE OR REPLACE FUNCTION round_time_10m(TIMESTAMP WITH TIME ZONE)
RETURNS TIMESTAMP WITH TIME ZONE AS $$
SELECT date_trunc('hour', $1) + INTERVAL '10 min' * ROUND(date_part('minute', $1) / 10.0)
$$ LANGUAGE SQL;
Generally rounding up to $2 minutes:
CREATE OR REPLACE FUNCTION round_time_nm(TIMESTAMP WITH TIME ZONE, INTEGER)
RETURNS TIMESTAMP WITH TIME ZONE AS $$
SELECT date_trunc('hour', $1) + ($2 || ' min')::INTERVAL * ROUND(date_part('minute', $1) / $2)
$$ LANGUAGE SQL;
here's an improved version of date_trunc
create cast (bigint as timestamptz) WITHOUT FUNCTION;
create cast (timestamptz as bigint) WITHOUT FUNCTION;
CREATE OR REPLACE FUNCTION date_trunc_by_interval( interval, timestamptz )
RETURNS timestamptz
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT
AS $$
select
case when $2::bigint >= 0::bigint then
$2::bigint - $2::bigint % (extract (epoch from $1)*1000000 ) ::bigint
else
$2::bigint - $2::bigint % (extract (epoch from $1)*1000000 ) ::bigint
- (extract (epoch from $1)*1000000 ) ::bigint
end ::timestamptz
$$;
this allows rounding to any fixed-length interval eg: '864 seconds' (divinding days into 100 parts) or '14 days' dividing the calendar into fortnights. the basis is '2000-01-01 00:00:00.0 +00' which is the epoch used to compute postgres
timestamp values.
it works by coercing the timestamptz value and the interval into bigints and doing integer arithmetic on them then coercing them back to timestamps
negative inputs need special handling (the case statement) as % causes rounding towards zero.
Postgres 14 date_bin.
Example use
SELECT date_bin('15 minutes', TIMESTAMP '2020-02-11 15:44:17', TIMESTAMP '2001-01-01');
Result: 2020-02-11 15:30:00
The timescaleDb extension has a time_bucket function that supports day, minutes and lower intervals.
Note: it does currently not support months, years: see #414