Postgres range select between timestamps - postgresql

Having two timestamps, and result in minutes between them lets say 320 minutes I need to calculate full hours, lets say we have here 5h and 20 minutes and I need to insert 6 rows with minutes column (5 rows with 60 as minutes column and last one with 20 minutes)
What is best way to do it in Postgres, some loops or trying to select numbers with cte?

demo:db<>fiddle
WITH timestamps AS (
SELECT '2019-01-07 03:30:00'::timestamp as ts1, '2019-01-07 08:50:00'::timestamp as ts2
)
SELECT 60 as minutes
FROM timestamps, generate_series(1, date_part('hour', ts2 - ts1)::int)
UNION ALL
SELECT date_part('minute', ts2 - ts1)::int
FROM timestamps
date_part extracts the hour (or minute) value from the timestamp difference.
with the generate_series function I am generating n rows with value 60 (n = hours)
Adding the remaining minutes with UNION ALL
Edit: For more than 1 day:
Instead of date_part use EXTRACT(EPOCH FROM ...) which gives you the difference in seconds.
WITH timestamps AS (
SELECT '2019-01-06 03:30:00'::timestamp as ts1, '2019-01-07 08:50:00'::timestamp as ts2
)
SELECT 60 as minutes
FROM timestamps, generate_series(1, (EXTRACT(EPOCH FROM ts2 - ts1) / 60 / 60)::int)
UNION ALL
SELECT (EXTRACT(EPOCH FROM ts2 - ts1) / 60)::int % 60
FROM timestamps
Calculate the seconds into hours with / 60 / 60
Calculate the remaining seconds with / 60 % 60 (first step gives you the minutes, the modulo operator % gives you the remaining minutes to hour)

Related

Extract() from Postgres to calculate minutes between 2 columns

Want to calculate minutes between to columns with start_time and end_time as timestamp without zone for two customers types, and then averange the result for each.
I tried to use extract() by using the following statement, but can't get the right result:
select avg(duration_minutes)
from (
select started_at,ended_at, extract('minute' from (started_at - ended_at)) as duration_minutes
from my_data
where customer_type = 'member'
) avg_duration;
Result:
avg
0.000
This run sucessfuly in BQ using the following:
select avg(duration_minutes) from
(
select started_at,ended_at,
datetime_diff(ended_at,started_at, minute) as duration_minutes
from my_table
where customer_type = "member"
) avg_duration
Result:
f0_
21.46
Wondering what might be failing in postgres?
extract(minute from ...) extracts the field with the minutes from the interval. So if the interval is "1 hour 26 minutes and 45 seconds" the result would be 26 not 86.
To convert an interval to the equivalent number of minutes, extract the total number of seconds using extract(epoch ...) and multiply that with 60.
select avg(duration_minutes)
from (
select started_at,
ended_at,
extract(epoch from (started_at - ended_at)) * 60 as duration_minutes
from my_data
where customer_type = 'member'
) avg_duration;
Note that you can calculate the average of an interval without the need to convert it to minutes:
select avg(duration)
from (
select started_at,
ended_at,
started_at - ended_at as duration
from my_data
where customer_type = 'member'
) avg_duration;
Depending on how you use the result, returning an interval might be more useful. You can also convert that to minutes using:
extract(epoch from avg(duration)) * 60 as average_minutes

How to fetch data in a given interval in postgresql

SELECT time,CEIL(AVG(value)) from table
where col1 = 1
and col2='matchThis'
and col3>='2022-04-10T18:30:00.00Z'
and col3<='2022-04-25T12:58:23.00Z'
and mod(to_char(col3, 'MI')::int, 15)=0
GROUP BY time
Semple response of the query to get 15-minute interval data
25-04-2022 01:00
25-04-2022 01:15
25-04-2022 01:30
25-04-2022 01:45
The above query works fine in 15, 30, and 60 minutes intervals but I have to create a query return interval data as the option shown below.
15 minutes
30 minutes
1 hour
2 hours
6 hours
12 hours
1 day
SELECT
ceil(avg(column_name)),
to_timestamp(floor((extract('epoch' from column_name) / 600 )) *600)
AT TIME ZONE 'UTC' as interval
FROM table_name
WHERE id=1
and column='value'
and col >='2022-04-21'
and col <= '2022-04-30'
GROUP BY interval ORDER BY interval ASC"

Postgresql difference in time based on interval

I have a table, time_slots, which has a column start_time. My users table has a column, hours_before (int), which is the number of hours before the start_time to receive a notification.
I'm running a job every 5 minutes that checks the time slots that have a start_time which is now + hours_before. All my datetimes are store in UTC. I can't figure out the correct where clause to get the appropriate time slots.
Right now I'm passing in the current time in UTC as a string rather than doing something like current_timestamp at time zone 'UTC'.
(extract(epoch from starts_at) - extract(epoch from (date '2020-11-09 06:20:00' + (users.hours_before * INTERVAL '1 hour')))) = 0
Here is a test query to see what the values are inside of a select. In this example, (6 * INTERVAL '1 hour') would be where users.hours_before would be. I'm using a static 6 while tinkering.
select
(extract(epoch from "starts_at") - extract(epoch from date '2020-11-09 06:20:00')) / 3600 as hours,
(extract(epoch from "starts_at") - extract(epoch from (date '2020-11-09 06:20:00' + (6 * INTERVAL '1 hour')))) as other_interval,
*
from
"time_slots"
where
"starts_at" > '2020-11-09 06:20:00'
order by hours asc;
For example, the following query has the same hours and other_interval values as the above query, despite the date being 2020-11-09 10:00:00 instead of 2020-11-09 06:00:00. Shouldn't those columns be 4 hours different since they change by 4 hours?
select
(extract(epoch from "starts_at") - extract(epoch from date '2020-11-09 10:00:00')) / 3600 as hours,
(extract(epoch from "starts_at") - extract(epoch from (date '2020-11-09 10:00:00' + (6 * INTERVAL '1 hour')))) as other_interval,
*
from
"time_slots"
where
"starts_at" >= '2020-11-09 06:00:00'
order by hours asc;

postgresql query for hour minutes and seconds

Hi I am having a Postgresql query like below to calculate DateTime difference for {1} and {2} in minutes.
CAST(ROUND(EXTRACT(EPOCH from (({2}::timestamp) - ({1}::timestamp)))/60) AS INT)
I want to calculate the difference in hours, minutes and seconds displayed like:
3 hrs 31 minutes 42 secs
What manipulation do I need for displaying like above?
SELECT to_char((col1 - col0), 'HH24 hrs MI "minutes" SS "seconds"') FROM T1;
Here is a sqlfiddle : link
The to_char function takes an interval (an interval is the time span between two timestamps, and subtracting timestamps gives you an interval). It then takes a formatting, and you can apply pretty much what you want.
Formatting functions in PostgreSQL
Try use this sql:
SELECT to_char(column2 - column1, 'DD" days "HH24" hours "MI" minutes "SS" seconds"');
The subtraction of two timestamp or timestamptz values produces an interval. (While subtracting two date values produces an integer!)
Details about date/time types in the manual.
The default text representation of an interval may be sufficient:
SELECT timestamp '2017-1-6 12:34:56' - timestamp '2017-1-1 0:0';
Result is an interval, displayed as:
5 days 12:34:56
If you need the format in the question, precisely, you need to specify how to deal with intervals >= 24 hours. Add 'days'? Or just increase hours accordingly?
#Nobody provided how to use to_char(). But add days one way or the other:
SELECT to_char(ts_col2 - ts_col1, 'DD" days "HH24" hours "MI" minutes "SS" seconds"');
Result:
05 days 12 hours 34 minutes 56 seconds
'days' covers the rest. There are no greater time units in the result by default.
Simple
SELECT
EXTRACT(year FROM LOCALTIMESTAMP(0) - yourFieldTime)||' year '||
EXTRACT(month FROM LOCALTIMESTAMP(0) - yourFieldTime)||' month '||
EXTRACT(day FROM LOCALTIMESTAMP(0) - yourFieldTime)||' day '||
EXTRACT(hour FROM LOCALTIMESTAMP(0) - yourFieldTime)||' hour '||
EXTRACT(minute FROM LOCALTIMESTAMP(0) - yourFieldTime)||' minute '||
EXTRACT(second FROM LOCALTIMESTAMP(0) - yourFieldTime)||' second '
AS full_time_as_you_wish FROM your_table;
Result
full_time_as_you_wish
---------------------------------
0 year 0 month 0 day 0 hour 0 minute 0 second

Checking for the minimum variability of a temporal database in postgresql

I have a table like this:
+------------+------------------+
|temperature |Date_time_of_data |
+------------+------------------+
| 4.5 |9/15/2007 12:12:12|
| 4.56 |9/15/2007 12:14:16|
| 4.44 |9/15/2007 12:16:02|
| 4.62 |9/15/2007 12:18:23|
| 4.89 |9/15/2007 12:21:01|
+------------+------------------+
The data-set contains more than 1000 records and I want to check for the minimum variability.
For every 30 minutes if the variance of temperature doesn't exceed 0.2, I want all the temperature values of that half an hour replaced by NULL.
Here is a SELECT to get the start of a period for every record:
SELECT temperature,
Date_time_of_data,
date_trunc('hour', Date_time_of_data)+
CASE WHEN date_part('minute', Date_time_of_data) >= 30
THEN interval '30 minutes'
ELSE interval '0 minutes'
END as start_of_period
FROM your_table
It truncates the date to hours (9/15/2007 12:12:12 to 9/15/2007 12:12:00)
and then adds 30 minutes if the date initially had more than 30 minutes.
Next - use start_of_period to group results and get min and max for every group:
SELECT temperature,
Date_time_of_data,
max(Date_time_of_data) OVER (PARTITION BY start_of_period) as max_temp,
min(Date_time_of_data) OVER (PARTITION BY start_of_period) as min_temp
FROM (previou_select_here)
Next - filter out the records, where the variance is more than 0.2
SELECT temperature,
Date_time_of_data
FROM (previou_select_here)
WHERE (max_temp - min_temp) <=0.2
And finally update your table
UPDATE your_table
SET temperature = NULL
WHERE Date_time_of_data IN (previous_select_here)
You may need to correct some spelling mistakes in this queries, before they work. I havent tested them.
And you can simplify them, if you need to.
P.S. If you need to filter out the data with variance less than 0.2 , you can simply create a VIEW from the third SELECT with
WHERE (max_temp - min_temp) > 0.2
And use the VIEW instead of table.
This query should do the job:
with intervals as (
select
date_trunc('hour', Date_time_of_data) + interval '30 min' * round(date_part('minute', Date_time_of_data) / 30.0) as valid_interval
from T
group by 1
having var_samp(temperature) > 0.2
)
select * from T
where
date_trunc('hour', Date_time_of_data) + interval '30 min' * round(date_part('minute', Date_time_of_data) / 30.0) in (select valid_interval from intervals)
The inner query (labeled as intervals) returns times when variance is over 0.2 (having var_samp(temperature) > 0.2). date_trunc ... expression rounds Date_time_of_data to half hour intervals.
The query returns nothing on the provided dataset.
create table T (temperature float8, Date_time_of_data timestamp without time zone);
insert into T values
(4.5, '2007-9-15 12:12:12'),
(4.56, '2007-9-15 12:14:16'),
(4.44, '2007-9-15 12:16:02'),
(4.62, '2007-9-15 12:18:23'),
(4.89, '2007-9-15 12:21:01')
;