How to generate series of 24hrs with 1 hour interval and display the last as 23:59:59 - sql

Project: BIRT
Datasource: Amazon Redshift
I want to generate a Data Set with value of:
00:00:00
1:00:00
2:00:00
3:00:00
4:00:00
5:00:00
6:00:00
7:00:00
8:00:00
9:00:00
10:00:00
11:00:00
12:00:00
13:00:00
14:00:00
15:00:00
16:00:00
17:00:00
18:00:00
19:00:00
20:00:00
21:00:00
22:00:00
23:00:00
23:59:59 //the last value should display like this
I was able to generate a series of 24hours with 1 hr interval, but I need to make the last one's value as 23:59:59
Query to generate 24 hours with 1 hour interval:
SELECT start_date + gs * interval '1 hour' as times
FROM (
SELECT '2019-05-21 00:00:00'::timestamp as start_date, generate_series(1,24, 1) as gs)
How is that?
Thanks

Updating your query, just adding a if for the last hour:
SELECT
start_date + gs * interval '1 hour'
- if(gs=24, interval '1 second', interval '0 second') as times
FROM (
SELECT
'2019-05-21 00:00:00'::timestamp as start_date
, generate_series(1,24, 1) as gs
)

I think too much about this, the simplest way to achieve this is just add a default value on the report parameter , if you're going to use the data set in the report parameter
or with this:
SELECT start_date + gs * interval '1 hour' as times
FROM (
SELECT '2020-01-01 00:00:00'::timestamp as start_date, generate_series(1,24, 1) as gs)
union
select '2020-01-01 23:59:59'::timestamp as start_date

Related

How to convert data from one row to multiple rows base on Date

I wish to convert data from one row to multiple rows base on start_time and end_time.
INPUT DATA:
ID
Start_Time
End_Time
Down_Mins
ABC123
11/22/2022 12:01
11/29/2022 14:33
10232.47
I need to write SQL for this requirement:
OUTPUT_DATA:
ID
Start_Time
End_Time
Down_Mins
ABC123
11/22/2022 12:01
11/23/2022 7:00
1138.55
ABC123
11/23/2022 7:00
11/24/2022 7:00
1440
ABC123
11/24/2022 7:00
11/25/2022 7:00
1440
ABC123
11/25/2022 7:00
11/26/2022 7:00
1440
ABC123
11/26/2022 7:00
11/27/2022 7:00
1440
ABC123
11/27/2022 7:00
11/28/2022 7:00
1440
ABC123
11/28/2022 7:00
11/29/2022 7:00
1440
ABC123
11/29/2022 7:00
11/29/2022 14:33
453.92
enter image description here
You can use a recursive query to split the data into rows for each 24-hour period starting at 7am:
WITH days (id, start_time, day_end, end_time, day_mins, down_mins) AS (
SELECT id,
start_time,
LEAST(TRUNC(start_time - INTERVAL '7' HOUR) + INTERVAL '31' HOUR, end_time),
end_time,
LEAST((LEAST(TRUNC(start_time - INTERVAL '7' HOUR) + INTERVAL '31' HOUR, end_time) - start_time) * 24 * 60, down_mins),
down_mins - LEAST((LEAST(TRUNC(start_time - INTERVAL '7' HOUR) + INTERVAL '31' HOUR, end_time) - start_time) * 24 * 60, down_mins)
FROM table_name
UNION ALL
SELECT id,
day_end,
LEAST(day_end + INTERVAL '24' HOUR, end_time),
end_time,
LEAST((LEAST(day_end + INTERVAL '24' HOUR, end_time) - day_end) * 24 * 60, down_mins),
down_mins - LEAST((LEAST(day_end + INTERVAL '24' HOUR, end_time) - day_end) * 24 * 60, down_mins)
FROM days
WHERE day_end < end_time
AND down_mins > 0
)
SEARCH DEPTH FIRST BY id, start_time SET order_id
SELECT id,
start_time,
day_end AS end_time,
day_mins AS down_mins
FROM days;
Which, for the sample data:
CREATE TABLE table_name (ID, Start_Time, End_Time, Down_Mins) AS
SELECT 'ABC123',
DATE '2022-11-23' + INTERVAL '7' HOUR - NUMTODSINTERVAL(1138.55, 'MINUTE'),
DATE '2022-11-23' + INTERVAL '7' HOUR + NUMTODSINTERVAL(10232.47 - 1138.55, 'MINUTE'),
10232.47
FROM DUAL;
Outputs:
ID
START_TIME
END_TIME
DOWN_MINS
ABC123
2022-11-22 12:01:27
2022-11-23 07:00:00
1138.55
ABC123
2022-11-23 07:00:00
2022-11-24 07:00:00
1440
ABC123
2022-11-24 07:00:00
2022-11-25 07:00:00
1440
ABC123
2022-11-25 07:00:00
2022-11-26 07:00:00
1440
ABC123
2022-11-26 07:00:00
2022-11-27 07:00:00
1440
ABC123
2022-11-27 07:00:00
2022-11-28 07:00:00
1440
ABC123
2022-11-28 07:00:00
2022-11-29 07:00:00
1440
ABC123
2022-11-29 07:00:00
2022-11-29 14:33:55
453.916666666666666666666666666666666667
fiddle

Oracle sql create agenda

I have a table with interval dates and times. Can i create a full list with this data?
Table example:
Start_Date, End_Date, Start_Time, End_Time, Interval
01-jun-2021 02-jun-2021 08:00 10:00 30
03-jun-2021 04-jun-2021 10:00 12:00 15
Result:
01-jun-2021 08:00
01-jun-2021 08:30
01-jun-2021 09:00
01-jun-2021 09:30
02-jun-2021 08:00
02-jun-2021 08:30
02-jun-2021 09:00
02-jun-2021 09:30
03-jun-2021 10:00
03-jun-2021 10:15
03-jun-2021 10:30
03-jun-2021 11:00
03-jun-2021 11:15
03-jun-2021 11:30
03-jun-2021 11:45
04-jun-2021 10:00
04-jun-2021 10:15
04-jun-2021 10:30
04-jun-2021 11:00
04-jun-2021 11:15
04-jun-2021 11:30
04-jun-2021 11:45
Thanks.
This is a handy place to use a recursive CTE:
with cte (start_date, end_date, interval) as (
select to_date(start_date||start_time, 'DD-Mon-YYYYHH24:MI'), to_date(end_date||end_time, 'DD-Mon-YYYYHH24:MI'), interval
from t
union all
select cte.start_date + cte.interval * interval '1' minute, end_date, interval
from cte
where cte.start_date < end_date
)
select cast(start_date as timestamp)
from cte
order by start_date;
Here is a db<>fiddle.
You can use a recursive CTE, but the logic has to skip to the next day when you reach the end time; so this works:
with rcte (date_time, end_date, start_int, end_int, step_int) as (
select
start_date + to_dsinterval('0 ' || start_time || ':00'),
end_date,
to_dsinterval('0 ' || start_time || ':00'),
to_dsinterval('0 ' || end_time || ':00'),
interval * interval '1' minute
from your_table
union all
select
case
when date_time + step_int < trunc(date_time) + end_int
then date_time + step_int
else trunc(date_time) + interval '1' day + start_int
end,
end_date,
start_int,
end_int,
step_int
from rcte
where date_time + step_int < end_date + end_int
)
select date_time
from rcte
order by date_time
DATE_TIME
-------------------
2021-06-01 08:00:00
2021-06-01 08:30:00
2021-06-01 09:00:00
2021-06-01 09:30:00
2021-06-02 08:00:00
2021-06-02 08:30:00
2021-06-02 09:00:00
2021-06-02 09:30:00
2021-06-03 10:00:00
2021-06-03 10:15:00
2021-06-03 10:30:00
2021-06-03 10:45:00
2021-06-03 11:00:00
2021-06-03 11:15:00
2021-06-03 11:30:00
2021-06-03 11:45:00
2021-06-04 10:00:00
2021-06-04 10:15:00
2021-06-04 10:30:00
2021-06-04 10:45:00
2021-06-04 11:00:00
2021-06-04 11:15:00
2021-06-04 11:30:00
2021-06-04 11:45:00
db<>fiddle showing the anchor member including converting the times and interval to real day to second intervals types for later use; the anchor and recursive members with all the intermediate columns; and finally just this version with a single column.
You can format the resulting date value however you want, of course.

Add 1 day to timezone aware timestamp with regards to daylight savings

I am trying to add 1 day to a timezone aware timestamp.
In this example I expected + interval '1' day to add 23 hours because DST starts on 2021-03-28 02:00:00 in Europe/Berlin, but it behaves the same as + interval '24' hour:
select timestamp '2021-03-28 00:00:00 Europe/Berlin' as before_dst,
timestamp '2021-03-28 00:00:00 Europe/Berlin' + interval '1' day as plus_1_day,
timestamp '2021-03-28 00:00:00 Europe/Berlin' + interval '24' hour as plus_24_hour
from dual;
BEFORE_DST
PLUS_1_DAY
PLUS_24_HOUR
2021-03-28 00:00:00.000000000 +01:00
2021-03-29 01:00:00.000000000 +02:00
2021-03-29 01:00:00.000000000 +02:00
Is there a way to add a day to a timestamp so that the beginnings or ends of daylight saving times are respected? For the example above that means a way to have oracle automatically recognize that the day 2021-03-28 only has 23 hours in Europe/Berlin.
I attempted to solve this by converting the timestamp to a local timestamp using at local before adding a day, but that does not work because at local converts the timestamp to the local time zone and not to something like a LocalDateTime in java, resulting in the exact same outcome: + interval '1' day always adding exactly 24 hours.
You could cast the timestamp with time zone value to a plain timestamp, which discards the time zone information; then add the 1-day interval, and declare the result to be in the required time zone:
from_tz(cast(timestamp '2021-03-28 00:00:00 Europe/Berlin' as timestamp) + interval '1' day, 'Europe/Berlin') as plus_1_day
or cast to a date (which could be implicit), add a day, and cast back:
from_tz(cast(cast(timestamp '2021-03-28 00:00:00 Europe/Berlin' as date) + 1 as timestamp), 'Europe/Berlin')
Adapting your example and showing the intermediate values:
select timestamp '2021-03-28 00:00:00 Europe/Berlin' as before_dst,
cast(timestamp '2021-03-28 00:00:00 Europe/Berlin' as timestamp) as as_ts,
cast(timestamp '2021-03-28 00:00:00 Europe/Berlin' as timestamp) + 1 as plus_1_day_ts,
from_tz(cast(timestamp '2021-03-28 00:00:00 Europe/Berlin' as timestamp) + interval '1' day, 'Europe/Berlin') as plus_1_day
from dual;
BEFORE_DST
AS_TS
PLUS_1_DAY_TS
PLUS_1_DAY
2021-03-28 00:00:00 +01:00
2021-03-28 00:00:00
2021-03-29 00:00:00
2021-03-29 00:00:00 +02:00
db<>fiddle
This assumes that you're always dealing with a fixed known time zone region; if you actually have a variable or column value with an unknown time zone then you can extract the region from that and use that as the from_tz() argument.
You should also be aware that this will work for your example at midnight, but won't work for all times. For example if your starting value was timestamp '2021-03-27 02:30:00 Europe/Berlin' then it would fail with "ORA-01878: specified field not found in datetime or interval", because it would end up try to declare 2021-03-28 02:30:00 to be in zone Europe/Berlin - and there is no such time, as that falls into the 'lost' hour of 02:00-03:00. Simply adding a day interval handles that - but then doesn't work as you expect in your example...
And this is because of this line in the documentation:
Oracle performs all timestamp arithmetic in UTC time.
2021-03-28 00:00:00 Europe/Berlin is 2021-03-27 23:00:00 UTC; adding a day to that is 2021-03-28 23:00:00 UTC; which is 2021-03-29 02:00:00 Europe/Berlin

sysdate format not working in where clause with time

I am trying to run following query on oracle at PL/SQL developer to select a list of time slots between current time and end of the day:
SELECT T.VISIT_DATE
FROM REGISTRATION.VU_SCHEDULE T
WHERE T.VISIT_DATE BETWEEN TO_DATE(SYSDATE, 'DD-MON-YYYY HH24:MI:SS')
AND TO_DATE('27-MARCH-2020 23:59:59', 'DD-MON-YYYY HH24:MI:SS')
ORDER BY VISIT_DATE
but it gives me result of whole day instead of current time of day
VISIT_DATE
1 3/27/2020 9:00:00 AM
2 3/27/2020 9:15:00 AM
3 3/27/2020 9:30:00 AM
4 3/27/2020 9:45:00 AM
5 3/27/2020 10:00:00 AM
6 3/27/2020 10:15:00 AM
7 3/27/2020 10:30:00 AM
8 3/27/2020 10:45:00 AM
9 3/27/2020 11:00:00 AM
10 3/27/2020 11:15:00 AM
11 3/27/2020 11:30:00 AM
12 3/27/2020 11:45:00 AM
e.g if current time is 11:00 AM then it should give result from current time.
I've tried trunc(sysdate) but it doesn't work
NOTE:
The condition must have date and time from now to the end of the day with format.
must have date and time from now to the end of the day with format.
You could do:
where t.visit_date >= sysdate and t.visit_date < trunc(sysdate) + 1
Rationale:
sysdate gives you the current date/time, that represents the lower bound of the interval
trunc(sysdate) is the beginninig of the current day (today at midnight), to which you can add 1 to get the beginning of the next day; this is the (exclusive) upper bound of the range
Note that there is no point applying to_date() to function sysdate, that produces a date alreay.

Return the end of the interval in Returning Functions?

In this case, 9.24. "Set Returning Functions" of the PostgreSQL 9.5 manual, only the initial dates and time are returned. Is it possible to return the date and time of the end of each interval?
SELECT * FROM generate_series('2008-03-01 00:00'::timestamp,
'2008-03-04 12:00', '10 hours');
generate_series
---------------------
2008-03-01 00:00:00
2008-03-01 10:00:00
2008-03-01 20:00:00
2008-03-02 06:00:00
2008-03-02 16:00:00
2008-03-03 02:00:00
2008-03-03 12:00:00
2008-03-03 22:00:00
2008-03-04 08:00:00
(9 rows)
Is this what you want?
SELECT gs.dte, LEAD(gs.dte) OVER (ORDER BY gs.dte) as next_dte
FROM generate_series('2008-03-01 00:00'::timestamp,
'2008-03-04 12:00',
'10 hours'
) gs(dte);
Or, if you don't want NULL for the last interval, explicitly do the calculation:
SELECT gs.dte, (gs.dte + interval '10 hours') as end_date
FROM generate_series('2008-03-01 00:00'::timestamp,
'2008-03-04 12:00',
'10 hours'
) gs(dte);