Get data for every second in interval - postgresql

I want to write a query which will results value between timestamp at every second and if value for particular time stamp is not there then it should result zero.for example
Start date time - 27/7/2015 10:00:00
End date time - 27/7/2015 10:05:00
Then result should be
27/7/2015 10:00:00 10 [start date time]
27/7/2015 10:00:01 19
27/7/2015 10:00:02 23
27/7/2015 10:00:03 0 [Value not present in table for this timestamp]
27/7/2015 10:00:04 45
27/7/2015 10:00:05 0 [Value not present in table for this timestamp]
...
27/7/2015 10:05:00 42 [end date time ]
I am trying this query but not getting desired result
SELECT CAST(date_trunc('second', CAST(to_timestamp(t1.timestamp_col,'DD-MM-YYYY HH24:MI:SS')as timestamp without time zone) + interval '1 second') as text)
, NULLIF(t1.y_temperature_col,'00')
FROM historical_trend_data t1
WHERE CAST(to_timestamp(t1.timestamp_col,'DD-MM-YYYY HH24:MI:SS') as timestamp without time zone) BETWEEN CAST(to_timestamp('28/7/2015 10:00:00','DD-MM-YYYY HH24:MI:SS')as timestamp without time zone) AND CAST(to_timestamp('28/7/2015 18:00:00','DD-MM-YYYY HH24:MI:SS') as timestamp without time zone);
This is the Function
CREATE OR REPLACE FUNCTION timestampwise_sp2(IN startdatetime text, IN enddatetime text,
OUT x_time_col text, OUT temperature text)
RETURNS SETOF record AS $BODY$
BEGIN
return query
with simul_data as(
SELECT generate_series(startdatetime::timestamp,
enddatetime::timestamp, '1 Seconds') As x_time_col
)
Select simul_data.x_time_col::text, coalesce(t1.y_temperature_col, '0') AS temperature
from historical_trend_data t1
LEFT JOIN simul_data ON CAST(to_timestamp(t1.timestamp_col,'DD-MM-YYYY HH24:MI:SS') as timestamp without time zone) = simul_data.x_time_col;
END; $BODY$ LANGUAGE plpgsql VOLATILE;
but it is not resulting the desired result

SELECT s.sec AS obs_time, coalesce(t1.y_temperature_col, 0) AS temperature
FROM generate_series('28-07-2015 10:00:00'::timestamp,
'28-07-2015 18:00:00'::timestamp, '1 minute') AS s(sec)
LEFT JOIN t1 ON timestamp_col = s.sec;
The generate_series() function returns a set of records in a sequence, in this case second intervals from a starting point to an ending point. That is left-joined to your actual data and any NULL values for the temperature are converted to 0 with the coalesce() function.
In function form it would be like this:
CREATE FUNCTION timestampwise_sp2(startdatetime timestamp, enddatetime timestamp) AS $$
SELECT to_char(simul_data.x_time_col, 'DD/MM/YYYY HH24:MI:SS') AS x_time_col,
coalesce(t1.y_temperature_col, '0') AS temperature
FROM generate_series($1, $2, '1 second') AS simul_data(x_time_col)
LEFT JOIN historical_trend_data t1
ON to_timestamp(t1.timestamp_col,'DD/MM/YYYY HH24:MI:SS') = simul_data.x_time_col;
$$ LANGUAGE sql STRICT;
Note that this is a sql language function, which is faster than plpgsql.

Related

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

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 can we declare a variable in cursor in postgresql

I want to store the result of fetch into a variable how we can do that in postgresql?
I also tried by creating a function it isn't working
code :
begin work;
DECLARE
cur_films CURSOR
FOR select CURRENT_DATE + i date_
FROM generate_series(date '2019-11-11'- CURRENT_DATE, date '2019-11-15' - CURRENT_DATE ) i;
fetch forward 2 from cur_films;
close cur_films;
commit work;
If you want to pass all values generated by your query to a function or procedure, you can aggregate everything into an array, then pass that array to the function:
DECLARE
l_dates date[];
begin
select array_agg(g.dt::date)
into l_dates
from generate_series(date '2019-11-11', date '2019-11-15', interval '1 day') as g(dt);
perform your_function(l_dates);
end;
But you wouldn't need PL/pgSQL for that. This can also be done in plain SQL:
with input as (
select array_agg(g.dt::date) as dates
from generate_series(date '2019-11-11', date '2019-11-15', interval '1 day') as g(dt)
)
select *
from your_function((select dates from input));

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