Access/SQL Interval breakout - sql

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

Related

SQL: Split time interval into 1 hour with overlapping minutes split (Bigquery)

This is the data that I have:
date
event_type
interval_start
interval_end
duration_in_min
2022-06-06
s1
09:05:00
11:45:00
160
2022-06-01
s2
08:00:00
08:17:00
17
2022-05-31
c1
17:55:00
18:08:00
13
2022-04-05
s3
07:58:00
08:46:00
48
...
and this is what I would like to achieve:
interval represents a 1 hour interval (or maybe 59 min and 59 sec to be accurate, in case an event starts/ends at exactly 10:00:00 but it should not occur very often).
date
interval
event_type
interval_start
interval_end
duration_in_min
2022-06-06
09:00:00
s1
09:05:00
11:45:00
55
2022-06-06
10:00:00
s1
09:05:00
11:45:00
60
2022-06-06
11:00:00
s1
09:05:00
11:45:00
45
2022-06-01
08:00:00
s2
08:00:00
08:17:00
17
2022-05-31
17:00:00
c1
17:55:00
18:08:00
5
2022-05-31
18:00:00
c1
17:55:00
18:08:00
8
2022-04-05
07:00:00
s3
07:58:00
08:46:00
2
2022-04-05
08:00:00
s3
07:58:00
08:46:00
46
...
I struggle to sort the data per hour by getting a split for the overlapping minutes into a new interval(s).
Any help would be greatly appreciated :)
Consider below approach
select
date, time(hour, 0, 0) as `interval`,
event_type, interval_start, interval_end,
time_diff(least(time(hour + 1, 0, 0), interval_end), greatest(time(hour, 0, 0), interval_start), minute) as duration_in_min
from your_table,
unnest(generate_array(0, 23)) hour
where hour between extract(hour from time(interval_start)) and extract(hour from time(interval_end))
if applied to sample data in your question - output is

Creating a Time Dimension table with Intervals in Oracle

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

SAS - PROC SQL: two tables: each one column distinct value, left join

I have a table with distinct dates YYYYMMDD from 20000101 until 20001231 and a table with distinct time points (HH:MM:SS) from 09:30:00 until 16:00:00.
I would like to create a (left) join where every day gets repeated 391 times assigned with each time point. That looks to me like a left join, however, I do not have any id's for joining.
date time
20000101 09:30:00
20000101 09:31:00
20000101 ...
20000101 ...
20000101 15:59:00
20000101 16:00:00
20000102 09:30:00
20000102 ...
20000102 16:00:00
how would the respective code look like (if there is no explicit common primary key to join on)?
PROC SQL;
SELECT DISTINCT a.date, b.time
FROM table_1 a, table_1 b (both information are in the same table)
;
QUIT;
Just as background: there are days that are "shorter" / less than 391 observation points. However, I would like to make sure every day has 391 observation points, just filled up with missing values.
You need Cartesian Product since you want to generate all combinations of date and time. So to produce such result you need CROSS JOIN in which you don't have to give any JOIN Condition.
Try the below query:
PROC SQL;
SELECT a.date, b.time
FROM table_1 a
CROSS JOIN
table_1 b
GROUP BY a.date, b.time
;
QUIT;
OR
PROC SQL;
SELECT a.date, b.time
FROM (SELECT date FROM table_1) a
CROSS JOIN
(SELECT time FROM table_1) b
GROUP BY a.date, b.time
;
QUIT;
For more info on CROSS JOIN Follow the below link:
http://support.sas.com/documentation/cdl/en/fedsqlref/67364/HTML/default/viewer.htm#p1q7agzgxs9ik5n1p7k3sdft0u9u.htm
You can do either a Left Join or Join and add Where 1=1 this will create the Cartesian Product for you:
Code:
proc sql;
create table want as
select t1.date, t2.time
from t1 left join t2 on 1=1
order by date, time;
quit;
To show all observed times (over all dates) for each date, as well as maintaining original satellite information I would use a reflexive cross join of the combinatoric columns for the basis of a reflexive left join.
Consider this sample data generator. It simulates the case of data being gathered at different intervals (every 10 or 20 minutes) on different days.
data have;
do i = 1 to 5;
date = '01-apr-2018'd + (i-1);
do j = 0 to 4;
time = '12:00't + (mod(i,2)+1) * 600 * j; * every other day sample at 1o or 20 minute interval;
x = ceil ( 25 * ranuni(123) );
OUTPUT;
end;
end;
format date yymmdd10. time time8.;
keep date time x;
run;
SQl is used to cross join the distinct dates and times and then the original data is left joined to the cross join.
proc sql;
create table cross_as_left_basis
as
select
cross.date
, cross.time
, have.x
from
( select distinct dates.date, times.time
from have as dates
cross join have as times
) as
cross
left join
have
on
cross.date = have.date
and cross.time = have.time
;
Have is
date time x
2018-04-01 12:00:00 19
12:20:00 9
12:40:00 5
13:00:00 23
13:20:00 9
2018-04-02 12:00:00 6
12:10:00 20
12:20:00 10
12:30:00 4
12:40:00 5
2018-04-03 12:00:00 20
12:20:00 11
12:40:00 25
13:00:00 7
13:20:00 18
2018-04-04 12:00:00 14
12:10:00 14
12:20:00 22
12:30:00 4
12:40:00 22
2018-04-05 12:00:00 17
12:20:00 20
12:40:00 18
13:00:00 9
13:20:00 14
The join result is
date time x
2018-04-01 12:00:00 19
12:10:00 .
12:20:00 9
12:30:00 .
12:40:00 5
13:00:00 23
13:20:00 9
2018-04-02 12:00:00 6
12:10:00 20
12:20:00 10
12:30:00 4
12:40:00 5
13:00:00 .
13:20:00 .
2018-04-03 12:00:00 20
12:10:00 .
12:20:00 11
12:30:00 .
12:40:00 25
13:00:00 7
13:20:00 18
2018-04-04 12:00:00 14
12:10:00 14
12:20:00 22
12:30:00 4
12:40:00 22
13:00:00 .
13:20:00 .
2018-04-05 12:00:00 17
12:10:00 .
12:20:00 20
12:30:00 .
12:40:00 18
13:00:00 9
13:20:00 14

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

SQL code for Comparing date fields in different rows and combining the results

I need help for proper Oracle SQL code to combine rows for a crystal reports command object. This is a part of the bigger query I'm working on and got stuck for the past couple of days.
for eg. if the columns are like below
PatId In_time Out_time
151 01/01/2012 07:00:00 am 01/01/2012 10:00:00 am
151 01/01/2012 11:00:00 am 01/02/2012 08:00:00 am
151 01/02/2012 11:00:00 am 01/02/2012 01:00:00 pm
151 01/03/2012 08:00:00 am 01/03/2012 03:00:00 pm
151 01/06/2012 03:30:00 pm 01/09/2012 07:00:00 am
167 01/03/2012 01:30:00 pm 01/09/2012 07:00:00 am
167 01/13/2012 03:30:00 pm 01/14/2012 07:00:00 am
167 01/14/2012 11:30:00 am 01/15/2012 11:30:00 am
167 01/18/2012 12:00:00 pm 01/19/2012 03:00:00 am
Within a PatId, the code should compare the Out_time of one row to the In_time of the next row, and check whether the time gap is greater than 48 hours. If not, then it is considered part of the same visit. I want one result row per PatID & visit, with min(In_time) and max(Out_time). The time span of the visit (result row) itself may be greater than 48 hours.
For this example, for PatId 151 the time difference between the out_time of 1st row and In_time of 2nd row is less than 48 hours. The difference between Out_time of second row and In_time of 3rd row, as well as between the 3rd and 4th rows, is also less than 48 hours. After this the gap between Out_time of the 4th row and In_time of 5th row is greater than 48 hours. The result for PatId 151 should be as below and same for EmpId 167, the chaining should continue until a gap greater than 48 hours is found.
So the result for the above table should be displayed as,
PatId In_time Out_time
151 01/01/2012 07:00:00 am 01/03/2012 03:00:00 pm
151 01/06/2012 03:30:00 pm 01/09/2012 07:00:00 am
167 01/03/2012 01:30:00 pm 01/09/2012 07:00:00 am
167 01/13/2012 03:30:00 pm 01/15/2012 11:30:00 am
167 01/18/2012 12:00:00 pm 01/19/2012 03:00:00 am
I could not get the logic on how to compare and merge rows.
Thanks in Advance, Abhi
General example of subtracting time - copy/paste to see the output. This example will give you differences in hours, minutes, seconds between two dates. The basic formula is (end_date - start_date) * 86400 (number of seconds in 24 hrs)...:
SELECT trunc(mydate / 3600) hr
, trunc(mod(mydate, 3600) / 60) mnt
, trunc(mod(mydate, 3600) / 60 /60) sec
FROM
(
SELECT (to_date('01/03/2012 10:00:00', 'mm/dd/yyyy hh24:mi:ss') -
to_date('01/01/2012 07:00:00', 'mm/dd/yyyy hh24:mi:ss')) * 86400 mydate
FROM dual
)
/
HR | MNT | SEC
---------------
51 | 0 | 0
You need to check your example and logic. I could not understand what needs to be comnpared with what...