Creating a Time Dimension table with Intervals in Oracle - sql

What is the easiest way to create a table in oracle to get the output as shown below;
Time Key
5 Minute Interval
10 Minute Interval
15 Minute Interval
100
12:00:00
12:10:00
12:15:00
101
12:05:00
12:10:00
12:15:00
102
12:10:00
12:20:00
12:15:00
103
12:15:00
12:20:00
12:30:00
The hours will be displayed in 24 hour format and will be used to join the time key onto a fact table to be loaded into a Microsoft Analysis Tabular Model.

Seems that you need a row generator. Here's an example.
Date format (just to know what you're looking at):
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
Query (creates 10 rows of data; change value in line #9 for more (or less) rows):
SQL> with temp (time_key, datum) as
2 (select 99, trunc(sysdate) datum from dual)
3 select
4 time_key + level time_key,
5 datum + ( 5 * (level - 1)) / (24 * 60) five_min,
6 datum + (10 * (level - 1)) / (24 * 60) ten_min,
7 datum + (15 * (level - 1)) / (24 * 60) fifteen_min
8 from temp
9 connect by level <= 10;
TIME_KEY FIVE_MIN TEN_MIN FIFTEEN_MIN
---------- ------------------- ------------------- -------------------
100 04.01.2021 00:00:00 04.01.2021 00:00:00 04.01.2021 00:00:00
101 04.01.2021 00:05:00 04.01.2021 00:10:00 04.01.2021 00:15:00
102 04.01.2021 00:10:00 04.01.2021 00:20:00 04.01.2021 00:30:00
103 04.01.2021 00:15:00 04.01.2021 00:30:00 04.01.2021 00:45:00
104 04.01.2021 00:20:00 04.01.2021 00:40:00 04.01.2021 01:00:00
105 04.01.2021 00:25:00 04.01.2021 00:50:00 04.01.2021 01:15:00
106 04.01.2021 00:30:00 04.01.2021 01:00:00 04.01.2021 01:30:00
107 04.01.2021 00:35:00 04.01.2021 01:10:00 04.01.2021 01:45:00
108 04.01.2021 00:40:00 04.01.2021 01:20:00 04.01.2021 02:00:00
109 04.01.2021 00:45:00 04.01.2021 01:30:00 04.01.2021 02:15:00
10 rows selected.
SQL>
In Oracle, there's no "time" datatype. We use date and it consists of both date AND time. Your example shows that you want time only; well, you can't have it, not as a date datatype. You can use e.g. TO_CHAR function on it (and fetch only time component). I wouldn't recommend you to store those values as strings into varchar2 datatype column as nothing prevents you (or someone else) to put e.g. 12:3f:75 into it, and that certainly isn't valid time value.
So, you'd then:
SQL> create table test as
2 with temp (time_key, datum) as
3 (select 99, trunc(sysdate) datum from dual)
4 select
5 time_key + level time_key,
6 datum + ( 5 * (level - 1)) / (24 * 60) five_min,
7 datum + (10 * (level - 1)) / (24 * 60) ten_min,
8 datum + (15 * (level - 1)) / (24 * 60) fifteen_min
9 from temp
10 connect by level <= 10;
Table created.
SQL> select time_key,
2 to_char(five_min, 'hh24:mi:ss') five_min
3 from test;
TIME_KEY FIVE_MIN
---------- --------
100 00:00:00
101 00:05:00
102 00:10:00
103 00:15:00
104 00:20:00
105 00:25:00
106 00:30:00
107 00:35:00
108 00:40:00
109 00:45:00
10 rows selected.
SQL>
To answer question T. Peter posted as a comment: I don't know for other databases, but - principle that should work elsewhere is to use a cross join (Cartesian product) with a subquery that returns "a lot of rows". In Oracle, ALL_OBJECTS is such a table. It isn't indefinite; in my sample schema, it contains ~8000 rows. For example (see lines #8 - 10):
SQL> with temp (time_key, datum) as
2 (select 99, trunc(sysdate) datum from dual)
3 select
4 time_key + rn time_key,
5 datum + ( 5 * (rn - 1)) / (24 * 60) five_min,
6 datum + (10 * (rn - 1)) / (24 * 60) ten_min,
7 datum + (15 * (rn - 1)) / (24 * 60) fifteen_min
8 from temp cross join (select rownum rn
9 from all_tables
10 where rownum <= 10
11 );
TIME_KEY FIVE_MIN TEN_MIN FIFTEEN_MIN
---------- ------------------- ------------------- -------------------
100 04.01.2021 00:00:00 04.01.2021 00:00:00 04.01.2021 00:00:00
101 04.01.2021 00:05:00 04.01.2021 00:10:00 04.01.2021 00:15:00
<snip>
Basically, keyword here is "row generator". I suggest you Google for it and add database name you use.

You can use a recursive sub-query factoring clause and get the time values using INTERVAL DAY TO SECOND data type:
CREATE TABLE table_name ( time_key, five_minute, ten_minute, fifteen_minute ) AS
WITH data ( time_key, five_minute, ten_minute, fifteen_minute, num_rows ) AS (
SELECT 100,
INTERVAL '12:00' HOUR TO MINUTE,
INTERVAL '12:00' HOUR TO MINUTE,
INTERVAL '12:00' HOUR TO MINUTE,
4
FROM DUAL
UNION ALL
SELECT time_key + 1,
five_minute + INTERVAL '5' MINUTE,
ten_minute + INTERVAL '10' MINUTE,
fifteen_minute + INTERVAL '15' MINUTE,
num_rows - 1
FROM data
WHERE num_rows > 1
)
SELECT time_key,
five_minute,
ten_minute,
fifteen_minute
FROM data;
Then the table contains:
TIME_KEY | FIVE_MINUTE | TEN_MINUTE | FIFTEEN_MINUTE
-------: | :---------------------------- | :---------------------------- | :----------------------------
100 | +000000000 12:00:00.000000000 | +000000000 12:00:00.000000000 | +000000000 12:00:00.000000000
101 | +000000000 12:05:00.000000000 | +000000000 12:10:00.000000000 | +000000000 12:15:00.000000000
102 | +000000000 12:10:00.000000000 | +000000000 12:20:00.000000000 | +000000000 12:30:00.000000000
103 | +000000000 12:15:00.000000000 | +000000000 12:30:00.000000000 | +000000000 12:45:00.000000000
db<>fiddle here

Related

Extract 5th Business Date from sysdate

I'm looking to extract the 5th Business Date data from database. Looking for pure 5th Business Date, no other business requirement like Holiday or New Year day.
Looking to extract 07/03/2022 from dual table using Oracle PL/SQL
Date
Day
Requirement
1/03/2022
Tuesday
1BD
2/03/2022
Wednesday
2BD
3/03/2022
Thursday
3BD
4/03/2022
Friday
4BD
5/03/2022
Saturday
Weekend
6/03/2022
Sunday
Weekend
7/03/2022
Monday
5BD
8/03/2022
Tuesday
6BD
9/03/2022
Wednesday
7BD
This is how I understood it.
Today is Thursday, 24.03.2022. It means that 5th business day looking backwards is Friday, 18.03.2022.
SQL> with test (datum, day) as
2 -- calendar
3 (select
4 trunc(sysdate) - &&par_number_of_days * 2 + level - 1,
5 to_char(trunc(sysdate) - &&par_number_of_days * 2 + level - 1, 'dy',
6 'nls_date_language = english')
7 from dual
8 connect by level <= (&&par_number_of_days * 2) + 1
9 ),
10 only_working_days as
11 -- remove weekends
12 (select datum,
13 day,
14 row_number() over (order by datum desc) rn
15 from test
16 where day not in ('sat', 'sun')
17 )
18 select datum, day, rn
19 from only_working_days
20 where rn = &&par_number_of_days;
Enter value for par_number_of_days: 5
DATUM DAY RN
---------- --- ----------
18.03.2022 fri 5
Or, 13th business day backwards is 08.03.2022:
SQL> undefine par_number_of_days
SQL> /
Enter value for par_number_of_days: 13
DATUM DAY RN
---------- --- ----------
08.03.2022 tue 13
SQL>
If it is, on the other hand, related to period since 1st of current, month, then
SQL> with test (datum, day) as
2 (select trunc(sysdate, 'mm') + level - 1,
3 to_char(trunc(sysdate, 'mm') + level - 1, 'dy', 'nls_date_language = english')
4 from dual
5 connect by level <= trunc(sysdate) - trunc(sysdate, 'mm') + 1
6 ),
7 only_working_days as
8 -- remove weekends
9 (select datum,
10 day,
11 row_number() over (order by datum) rn
12 from test
13 where day not in ('sat', 'sun')
14 )
15 select datum, day, rn
16 from only_working_days
17 where rn = &par_number_of_days;
Enter value for par_number_of_days: 5
DATUM DAY RN
---------- --- ----------
07.03.2022 mon 5
SQL> /
Enter value for par_number_of_days: 13
DATUM DAY RN
---------- --- ----------
17.03.2022 thu 13
SQL>
The 5th business day will always be 7 days ahead, since there will be 5 weekdays and 2 weekend days, so the simplest solution is:
SELECT TRUNC(SYSDATE) + INTERVAL '7' DAYS
FROM DUAL
More generally, if you want to add a number of business days to a date then you can calculate it using:
start_date
+ FLOOR(bd/5) * INTERVAL '7' DAY -- Full weeks
+ MOD(bd, 5) -- Part week
+ CASE
WHEN start_date - TRUNC(start_date, 'IW') + MOD(bd, 5) >= 5
THEN 2
WHEN start_date - TRUNC(start_date, 'IW') + MOD(bd, 5) < 0
THEN -2
ELSE 0
END -- Adjust for weekend
For example, given the sample data:
CREATE TABLE table_name (start_date, bd) AS
SELECT TRUNC(SYSDATE), LEVEL - 11 FROM DUAL CONNECT BY LEVEL <= 21
UNION ALL
SELECT DATE '2022-03-01', 5 FROM DUAL;
Then:
SELECT start_date,
bd,
start_date
+ FLOOR(bd/5) * INTERVAL '7' DAY -- Full weeks
+ MOD(bd, 5) -- Part week
+ CASE
WHEN start_date - TRUNC(start_date, 'IW') + MOD(bd, 5) >= 5
THEN 2
WHEN start_date - TRUNC(start_date, 'IW') + MOD(bd, 5) < 0
THEN -2
ELSE 0
END -- Adjust for weekend
AS adjusted_business_day
FROM table_name;
Outputs:
START_DATE
BD
ADJUSTED_BUSINESS_DAY
2022-03-24 00:00:00 (THU)
-10
2022-03-10 00:00:00 (THU)
2022-03-24 00:00:00 (THU)
-9
2022-03-04 00:00:00 (FRI)
2022-03-24 00:00:00 (THU)
-8
2022-03-07 00:00:00 (MON)
2022-03-24 00:00:00 (THU)
-7
2022-03-08 00:00:00 (TUE)
2022-03-24 00:00:00 (THU)
-6
2022-03-09 00:00:00 (WED)
2022-03-24 00:00:00 (THU)
-5
2022-03-17 00:00:00 (THU)
2022-03-24 00:00:00 (THU)
-4
2022-03-11 00:00:00 (FRI)
2022-03-24 00:00:00 (THU)
-3
2022-03-14 00:00:00 (MON)
2022-03-24 00:00:00 (THU)
-2
2022-03-15 00:00:00 (TUE)
2022-03-24 00:00:00 (THU)
-1
2022-03-16 00:00:00 (WED)
2022-03-24 00:00:00 (THU)
0
2022-03-24 00:00:00 (THU)
2022-03-24 00:00:00 (THU)
1
2022-03-25 00:00:00 (FRI)
2022-03-24 00:00:00 (THU)
2
2022-03-28 00:00:00 (MON)
2022-03-24 00:00:00 (THU)
3
2022-03-29 00:00:00 (TUE)
2022-03-24 00:00:00 (THU)
4
2022-03-30 00:00:00 (WED)
2022-03-24 00:00:00 (THU)
5
2022-03-31 00:00:00 (THU)
2022-03-24 00:00:00 (THU)
6
2022-04-01 00:00:00 (FRI)
2022-03-24 00:00:00 (THU)
7
2022-04-04 00:00:00 (MON)
2022-03-24 00:00:00 (THU)
8
2022-04-05 00:00:00 (TUE)
2022-03-24 00:00:00 (THU)
9
2022-04-06 00:00:00 (WED)
2022-03-24 00:00:00 (THU)
10
2022-04-07 00:00:00 (THU)
2022-03-01 00:00:00 (TUE)
5
2022-03-08 00:00:00 (TUE)
db<>fiddle here

Total time calculation in a sql query for a day where time in 24 hour format as hhmm

I have a table with date(date), left time(varchar2(4)) and arrival time(varchar2(4)). Time taken is in 24 hour format as hhmm. If a person travel 3 times a day, what will be the query to calculate total travel time in a day?
I am using oracle 11g. Kindly help. Thank you.
Convert the value to a number and report in minutes:
select to_number(substring(time, 1, 2))*60 + to_number(substring(time, 3, 2)) as minutes
Your query would look something like:
select person, sum(to_number(substring(time, 1, 2))*60 + to_number(substring(time, 3, 2))) as minutes
from t
group by person;
I see no reason to convert this back to a string -- or to even store the value as a string instead of as a number. But if you need to, you can reverse the process to get a string.
There are 2 answers, If you want to sum time only on date then it can be done as:-
select curr_date,
sum(24 * (to_date(arrival_time, 'HH24:mi:ss')- to_date(left_time, 'HH24:mi:ss'))) as difference
from sql_prac group by curr_date,arrival_time,left_time;
The sample output is as follows:-
select curr_date,left_time,arrival_time from sql_prac;
CURR_DATE LEFT_TIME ARRIVAL_TIME
--------- -------------------- --------------------
30-JUN-17 00:00:00 15:00:00
30-JUL-17 03:30:00 11:30:00
30-AUG-17 03:00:00 12:30:00
30-SEP-17 04:00:00 17:00:00
30-JUN-17 00:00:00 15:00:00
30-JUL-17 03:30:00 11:30:00
30-AUG-17 03:00:00 12:30:00
30-SEP-17 04:00:00 17:00:00
30-SEP-17 04:00:00 17:00:00
9 rows selected
select curr_date,sum(24 * (to_date(arrival_time, 'HH24:mi:ss')- to_date(left_time, 'HH24:mi:ss'))) as difference
from sql_prac group by curr_date,arrival_time,left_time;
CURR_DATE DIFFERENCE
--------- ----------
30-JUN-17 30
30-JUL-17 16
30-SEP-17 39
30-AUG-17 19
If you want to sum it by person and date then it can be done as:-
select dept,curr_date,sum(24 * (to_date(arrival_time, 'HH24:mi:ss')- to_date(left_time, 'HH24:mi:ss'))) as difference
from sql_prac group by dept,curr_date,arrival_time,left_time order by Dept;
The sample output is as follows:-
Data in table is:-
select dept,curr_date,left_time,arrival_time from sql_prac;
DEPT CURR_DATE LEFT_TIME ARRIVAL_TIME
-------------------- --------- -------------------- --------------------
A 30-SEP-17 04:00:00 17:00:00
B 30-SEP-17 04:00:00 17:00:00
C 30-AUG-17 03:00:00 12:30:00
D 30-DEC-17 04:00:00 17:00:00
A 30-SEP-17 04:00:00 17:00:00
B 30-JUL-17 03:30:00 11:30:00
C 30-AUG-17 03:00:00 12:30:00
D 30-SEP-17 04:00:00 17:00:00
R 30-SEP-17 04:00:00 17:00:00
Data fetched using the query
select dept,curr_date,sum(24 * (to_date(arrival_time, 'HH24:mi:ss')- to_date(left_time, 'HH24:mi:ss'))) as difference
from sql_prac group by dept,curr_date,arrival_time,left_time order by Dept;
DEPT CURR_DATE DIFFERENCE
-------------------- --------- ----------
A 30-SEP-17 26
B 30-JUL-17 8
B 30-SEP-17 13
C 30-AUG-17 19
D 30-SEP-17 13
D 30-DEC-17 13
R 30-SEP-17 13

How to group by hour in HANA

I have the following table in HANA :
vehicle_id time roaming_time parking_time
1 Sep 01,2016 3:09:03 AM 3 9
2 Sep 01,2016 3:12:03 AM 6 8
1 Sep 01,2016 9:10:03 AM 10 6
4 Sep 01,2016 10:09:03 AM 9 3
1 Sep 01,2016 10:10:03 AM 10 10
4 Sep 01,2016 12:09:03 AM 3 9
from these information I wanted to know that what is the sum of roaming_time and sum of parking_time for each hour from all the vehicles and want the output in the format:
time roaming_time parking_time
____ _____________ ____________
2016-09-01 00:00:00 3 9
2016-09-01 01:00:00 6 8
2016-09-01 02:00:00 9 6
2016-09-01 03:00:00 3 6
2016-09-01 04:00:00 12 3
2016-09-01 05:00:00 15 8
2016-09-01 06:00:00 18 4
2016-09-01 07:00:00 8 3
2016-09-01 08:00:00 9 4
2016-09-01 09:00:00 6 6
2016-09-01 10:00:00 6 9
........
2016-09-01 23:00:00 3 12
I need to group the following query which gives all the sum by hour wise and get the expected result:
select sum(roaming_time) as roaming_time,sum(parking_time) as parking_time
from time>='2016-09-01 00:00:00'
time>='2016-09-01 23:59:59'
I do not know how to do the grouping by hour in HANA. Any help is appreciated
Here is one method . . . it converts the time to a date and hour format:
select to_varchar(time, 'YYYY-MM-DD'), hour(time),
sum(roaming_time) as roaming_time, sum(parking_time) as parking_time from t
group by date(time), hour(time)
order by to_varchar(time, 'YYYY-MM-DD'), hour(time);
Use a group by clause with SERIES_ROUND(). Avoid date() and hour() and similar data/time functions on large data sets as they tend to be slower.
select SERIES_ROUND(time, 'INTERVAL 1 HOUR') as time,
sum(roaming_time) as roaming_time, sum(parking_time) as parking_time from t
group by SERIES_ROUND(time, 'INTERVAL 1 HOUR')
order by SERIES_ROUND(time, 'INTERVAL 1 HOUR');
Another approach is to convert it to a string, especially if no further time calculations are required.
This could look like this:
select to_varchar(time, 'DD.MM.YYYY HH24') as parking_hour ,
sum(roaming_time) as roaming_time, sum(parking_time) as parking_time from t
group by to_varchar(time, 'DD.MM.YYYY HH24') as parking_hour
order byto_varchar(time, 'DD.MM.YYYY HH24') as parking_hour;

How to Aggregate timestamp Values to 15 MIns?

I have some values for some particular timestam like
Time Value
18/10/2016 00:00:00 10
18/10/2016 00:05:00 20
18/10/2016 00:10:00 30
18/10/2016 00:15:00 40
18/10/2016 00:20:00 50
18/10/2016 00:25:00 60
18/10/2016 00:30:00 70
I want to aggregate it to 15 Mins data. My aggregation should be like at 18/10/2016 00:15:00 it should take the average of 5mins,10Mins and 15Mins to give 15th Min data.
My output should be like
Time value
18/10/2016 00:15:00 30
18/10/2016 00:30:00 60
Is there any way to get this in oracle?
Here is one method:
select (trunc(time, 'HOUR') + 15 * trunc(extract(minute from time) / 15) / (60 * 24)) as time,
sum(value)
from t
group by (trunc(time, 'HOUR') + 15 * trunc(extract(minute from time) / 15) / (60 * 24));

Access/SQL Interval breakout

EDIT: SIMPLIED CODE
CODE START END TLY
X 7/12/2016 10:30:00 AM 7/12/2016 11:00:00 AM 68
X 8/14/2016 09:30:00 AM 8/14/2016 11:00:00 AM 11
I was looking for some assistance with an odd table that I have pulled into Access (using SQL to query). The underlying issue I have is sometimes the data can be every 30 minutes as represented in line one (10:00-10:30) or it could be multiple intervals such as line two 09:30-11:00.
Ideally, I'd like to represent this as every 30 minute period they are reporting for so the data would look as follows:
CODE START END TLY
X 7/12/2016 10:30:00 AM 7/12/2016 11:00:00 AM 68
X 8/14/2016 09:30:00 AM 8/14/2016 10:00:00 AM 11
X 8/14/2016 10:00:00 AM 8/14/2016 10:30:00 AM 11
X 8/14/2016 10:30:00 AM 8/14/2016 11:00:00 AM 11
Code used:
SELECT
SGT.CODE AS CODE, CVDate((SGT.START_MOMENT-240)/1440) AS START,
CVDate((SGT.STOP_MOMENT-240)/1440) AS END,
SGT.TLY AS TLY
FROM
STF_GRP_TLY AS SGT
Any thoughts or suggestions that I could try?
Thank you!
George
Leaving aside your joins and date/time math [... - 240)/1440] for the moment, if you have a data table
[tblIntervals]
CODE START END TLY
------------ ------------------- ------------------- ---
Group_Sample 2016-07-12 10:30:00 2016-07-12 11:00:00 68
Group_Sample 2016-08-14 09:30:00 2016-08-14 11:00:00 11
and you create a "numbers table" that starts with zero and counts up (by one) to more than the maximum number of half-hour subintervals you would ever expect to encounter, e.g.
[tblNumbers]
n
--
0
1
2
3
4
5
6
7
8
9
then you can create a query that uses a cross-join to list the individual sub-intervals like so:
SELECT
tblIntervals.CODE,
DateAdd("n", 30 * tblNumbers.n, tblIntervals.START) AS dtmSTART,
DateAdd("n", 30 * (tblNumbers.n + 1), tblIntervals.START) AS dtmEND,
tblIntervals.TLY
FROM
tblIntervals, tblNumbers
WHERE
DateAdd("n", 30 * tblNumbers.n, tblIntervals.START) < tblIntervals.END
ORDER BY 1, 2
returning
CODE dtmSTART dtmEND TLY
------------ ------------------- ------------------- ---
Group_Sample 2016-07-12 10:30:00 2016-07-12 11:00:00 68
Group_Sample 2016-08-14 09:30:00 2016-08-14 10:00:00 11
Group_Sample 2016-08-14 10:00:00 2016-08-14 10:30:00 11
Group_Sample 2016-08-14 10:30:00 2016-08-14 11:00:00 11
Edit
To incorporate the conversion from "nominal" to "real" date/time values you could use a query like this:
SELECT
STF_GRP_TLY.STF_GRP_SK,
DateAdd("n", 30 * tblNumbers.n, CDate((STF_GRP_TLY.START_MOMENT-240)/1440)) AS dtmSTART,
DateAdd("n", 30 * (tblNumbers.n + 1), CDate((STF_GRP_TLY.START_MOMENT-240)/1440)) AS dtmEND,
STF_GRP_TLY.TLY
FROM STF_GRP_TLY, tblNumbers
WHERE
DateAdd("n", 30 * tblNumbers.n, CDate((STF_GRP_TLY.START_MOMENT-240)/1440)) < CDate((STF_GRP_TLY.STOP_MOMENT-240)/1440)
although I would be inclined to create a saved query that does the conversion to real date/time values ...
[qryIntervals]
SELECT
STF_GRP_SK,
CDate((START_MOMENT-240)/1440) AS START,
CDate((STOP_MOMENT-240)/1440) AS END,
TLY
FROM STF_GRP_TLY
... and then use it in the following simplified query to generate the sub-intervals
SELECT
qryIntervals.STF_GRP_SK,
DateAdd("n", 30 * tblNumbers.n, qryIntervals.START) AS dtmSTART,
DateAdd("n", 30 * (tblNumbers.n + 1), qryIntervals.START) AS dtmEND,
qryIntervals.TLY
FROM qryIntervals, tblNumbers
WHERE
DateAdd("n", 30 * tblNumbers.n, qryIntervals.START) < qryIntervals.END