Postgres: Select a timeinterval that spans past midnight - sql

I have the following table:
id | time
----+-------------
1 | 21:00:00+01
2 | 22:00:00+01
3 | 23:00:00+01
Column id is of type integer and time is time with timezone. I want to select all rows that fall within a specified interval, e.g.,
select *
from times
where time >= time '22:30' - interval '60 minutes' and time <= time '22:30' + interval '60 minutes';
However, if the intervall extends past midnight, i.e., when I select 23:30 as time argument, then I get an empty result set.
Is there a way to tell postgress to ignore the minutes that span past midnight?

You can use this logic:
select *
from times t cross join
(values ('22:30'::time - interval '60 minutes', '22:30'::time + interval '60 minutes')
) v(fromt, tot)
where (fromt <= tot and time >= fromt and time <= tot) or
(fromt > tot and (time >= fromt or time <= tot))

Related

How to simplify the search with an interval in an SQl query?

Please tell me if it is somehow easier to write this query where the check occurs at intervals?
SELECT type, time_from, time_to
FROM manager_orders
WHERE aptid = 262707
AND cancelled_at is null
AND ('2021-04-26 11:00:00'
BETWEEN time_from - INTERVAL 30 Minute
AND time_to + INTERVAL 30 Minute
OR '2021-04-26 11:00:00'
BETWEEN time_from - INTERVAL 30 Minute
AND time_to + INTERVAL 60 Minute
)

get time series in 10 minutes of interval

I am generating one time-series from using below query.
SELECT date_trunc('min', dd):: TIMESTAMP WITHOUT TIME zone as time_ent
FROM generate_series ( timestamp '2021-12-09 06:34:37' + ((DATE_PART('min', timestamp '2021-12-09 06:34:37')::integer % 2) || ' minutes') :: INTERVAL
, '2021-12-10 06:34:37'::timestamp
, '20 min'::interval) dd
and it will give me output like below.
2021-12-09 06:34:00.000
2021-12-09 06:54:00.000
2021-12-09 07:14:00.000
2021-12-09 07:34:00.000
but I need output like.
2021-12-09 06:40:00.000
2021-12-09 07:00:00.000
2021-12-09 07:20:00.000
2021-12-09 07:40:00.000
currently, the time series hours depend upon the timestamp that I pass. in above it gives me mins like 34,54,14...but I want the mins like 40,00,20...it should not depend on the time I passed in query. I tried with timestamp '2021-12-09 06:34:37' + ((DATE_PART('min', timestamp '2021-12-09 06:34:37')::integer % 2) || ' minutes') :: INTERVAL but not any success.
Based on your description, I assumed that you want to create a series of timestamps for 00, 20, 40 minute at each hour until the next day.
select *
from (
select date_trunc('hour', current_timestamp) + i * interval '20 minutes' as ts
from generate_series(1, 24*3) as t(i)) t
where ts between current_timestamp and current_timestamp + interval '1 day'
The key idea here is to truncate the current_timestamp to 00 minute first. This becomes the start of the series. Then filter out the generated timestamps outside the range you want. You may need to adjust the second argument of generate_series function, depending on your requirement.
Or you can just generate a series of timestamp like the following:
select *
from (
select ts
from generate_series(
date_trunc('hour', current_timestamp),
current_timestamp + interval '1 day',
interval '20 minutes') as t(ts)) as t
where ts between current_timestamp and current_timestamp + interval '1 day'
Here, you still need to trunc your timestamp to hour first so the start of the series at 00 minute.

Get daily count of rows for a Time

My database table looks like this:
CREATE TABLE record
(
id INT,
status INT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
And I want to create a generic query to get count of record created after 3 hours of interval in last day
For example, I want to know in last 1 day after 3 hours how many records are created.
What I have so far: with a little help from stackoverflow I am able to create a query to calculate the count for a single full day.
SELECT
DATE(created_at) AS day, COUNT(1)
FROM
record
WHERE
created_at >= current_date - 1
GROUP BY
DATE(created_at)
This is telling me in full day like 24 records are created but I want to get how many are made in interval of 3 hours
If you want the count for the last three hours of data:
select count(*)
from record
where created_at >= now() - interval '3 hour';
If you want the last day minus 3 hours, that would be 21 hours:
select count(*)
from record
where created_at >= now() - interval '21 hour';
EDIT:
You want intervals of 3 hours for the last 24 hours. The simplest method is probably generate_series():
select gs.ts, count(r.created_at)
from generate_series(now() - interval '24 hour', now() - interval '3 hour', interval '3 hour') gs(ts) left join
record r
on r.created_at >= gs.ts and
r.created_at < gs.ts + interval '3 hour'
group by gs.ts
order by gs.ts;

import data between specific times from consecutive days

In BIRT Report (which executes query for 10 minutes automatically - 24x7), I would like to report data of datatype "Timestamp", which I import from a table in oracle databank using query. Let's say I have a table with name "table 1". Table 1 contains a column with name "column1" of datatype timestamp. The data in column1 is from a machine which runs continuously (24x7 - 365 days).
Everyday in the report, I would like to present only 24 hours data, which is between 05:30:00 (present day) and 05:30:00 (next day).
SELECT column1 from table 1
WHERE coulmn1 = SYSDATE - INTERVAL '24' hour;
Extracting data of last 24 hours or days or months is easy, but how to define a logic in where statement, which takes the actual systemtime as a reference and provides data between 05:30 (present day) and 05:30 (next day).
For Example:
The automatic execution of query at 01:00:00 (or 1 AM) should show the data from previous day (05:30:00) to present (01:00:00)
The automatic executin of query at 08:00:00 (or 8 AM) should show the data from actual day (05:30:00 to 08:00:00).
Any help how to define a logic in where statement will be appreciated.
Use TRUNC(SYSDATE) to trunate it back to midnight and then add an INTERVAL:
SELECT column1
FROM table1
WHERE column1 >= TRUNC( SYSDATE ) + INTERVAL '0 05:30' DAY TO MINUTE
AND column1 < TRUNC( SYSDATE ) + INTERVAL '1 05:30' DAY TO MINUTE;
If you want to handle the case when the SYSDATE is before 05:30 then:
SELECT column1
FROM table1
WHERE column1 >= TRUNC( SYSDATE - INTERVAL '05:30' HOUR TO MINUTE )
+ INTERVAL '0 05:30' DAY TO MINUTE
AND column1 < TRUNC( SYSDATE - INTERVAL '05:30' HOUR TO MINUTE )
+ INTERVAL '1 05:30' DAY TO MINUTE;
And if you do not want to show future times then:
SELECT column1
FROM table1
WHERE column1 >= TRUNC( SYSDATE - INTERVAL '05:30' HOUR TO MINUTE )
+ INTERVAL '0 05:30' DAY TO MINUTE
AND column1 <= SYSTIMESTAMP;

SQL statement dynamically using current time to choose a time frame in a field (Oracle)

All, I have something that is stumping me and I have seen a lot of examples, but nothing is helping solve this.
I have time frames like 03:30:00 to 11:29:59 that I work with (say shift times). I want to dynamically query data for the last shift based on the current shift.
Example: if it is currently between 11:30:00 AM and 7:29:59 PM, I want get the last shift that was between 03:30:00 AM and 11:30:00 AM.
This would look like an if statement in my mind:
If time between .... then
select time between....
elseif time between.... then
select time between...
I tried many combinations and can't figure this out. I think I would need a CASE and maybe a subquery? or maybe DECODE will work?
SELECT CAST(ccd.DATEc AS TIME) as time_occured,
FROM db.datatb ccd
WHERE ccd.DATE > SYSDATE - interval '1440' minute
AND (
((TO_CHAR(SYSDATE, 'hh24:mi:ss')BETWEEN '03:30:00' AND '11:29:59' IN (SELECT
ccd.DATEc FROM db.datatb WHERE (CAST(ccd.DATEc AS TIME)NOT BETWEEN '03:30:00
AM' AND '07:29:59 PM')))
OR (TO_CHAR(SYSDATE, 'hh24:mi:ss')BETWEEN '11:30:00' AND '19:29:59' IN
(SELECT ccd.DATEc FROM db.datatb WHERE (CAST(ccd.DATEc AS TIME) BETWEEN
'03:30:00 AM' AND '11:29:59 AM')))
OR (TO_CHAR(SYSDATE, 'hh24:mi:ss')NOT BETWEEN '03:30:00' AND '19:29:59' IN
(SELECT ccd.DATEc FROM db.datatb WHERE (CAST(ccd.DATEc AS TIME) BETWEEN
'11:30:00 AM' AND '07:29:59 PM')))
)
SELECT *
FROM db.datatb
CROSS JOIN
( SELECT TRUNC( SYSDATE - INTERVAL '210' MINUTE )
+ NUMTODSINTERVAL(
TRUNC(
( SYSDATE - INTERVAL '210' MINUTE
- TRUNC( SYSDATE - INTERVAL '210' MINUTE )
) * 3
) * 480
+ 210,
'MINUTE'
) AS current_shift_start
FROM DUAL
) css
WHERE DATEc >= css.current_shift_start - INTERVAL '8' HOUR
AND DATEc < css.current_shift_start;
Explanation:
The shifts are 8 hours each starting at 03:30 (or 210 minutes past midnight); so SYSDATE - INTERVAL '210' MINUTE will move offset the times so that after this offset they start at 00:00, 08:00 and 16:00 which is thirds of a day.
date_value - TRUNC( date_value ) calculates the fraction of a day (between 0 and 1) that the time component represents; so TRUNC( ( date_value - TRUNC( date_value ) ) * 3 ) maps that fraction of the day to 0, 1 or 2 corresponding to whether it is in the 1st, 2nd or 3rd 8 hour period of the day. Multiple that value by 480 minutes and then add the 210 minutes that the date was originally offset by and you have the minutes past the start of the day that the shift starts.