Calculate difference between endTime from ID 1 and startTime from ID 2 - sql

id startTime endTime
1 2022-12-3 13:00:00 2022-12-3 14:00:00
2 2022-12-3 14:00:00 2022-12-3 14:30:00
3 2022-12-3 15:00:00 2022-12-3 15:15:00
4 2022-12-3 15:30:00 2222-12-3 16:30:00
5 2022-12-3 18:30:00 2022-12-3 19:00:00
SELECT startTime, endTime,
(TIMESTAMPDIFF(MINUTE, startTime , endTime) = '60') AS MinuteDiff
FROM booking
OUTPUT:
id startTime endTime MinuteDiff
1 2022-12-3 13:00:00 2022-12-3 14:00:00 1
2 2022-12-3 14:00:00 2022-12-3 14:30:00 0
3 2022-12-3 15:00:00 2022-12-3 15:15:00 0
4 2022-12-3 15:30:00 2022-12-3 16:30:00 1
5 2022-12-3 18:30:00 2022-12-3 19:00:00 0
I am calculating the difference between the startTime and endTime of ID 1, how to calculate the difference between the endTime of ID 1 and the startTime of ID 2, and so on?

Do try this one:
If you want your last row to be included in your result, use LEFT JOIN, if you don't want to include the last row use 'JOIN'.
SELECT d.`id`,
d.`endTime`,
IFNULL(d1.`startTime`,d.`endTime`),
IFNULL(TIMESTAMPDIFF(MINUTE, d.endTime, d1.startTime),0) FROM date_table d LEFT
JOIN date_table d1 ON d1.`id`=d.`id`+1
Or you can use following with Windows Functions:
SELECT
id,
endTime,
lead(startTime) over (order by id) nextStartDate,
TIMESTAMPDIFF(MINUTE,endTime,lead(startTime) over (order by id)) as timeDiff
FROM
date_table d;

Related

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.

SQL query to select the start and end datetime of a value with system versioned tables

Basically, I want to use system versioned tables to find out the start and end date all users held a position within a company.
I'm struggling with the amount of other changes made to the record (Other field changes that create a new versioned record).
I originally tried to Group By UserId, CompanyId, Position and then take the min SysStartTime and max SysEndTime. Which at first glance did work. However it does not work if a position is changed back to its original value.
SELECT DISTINCT
cu.UserId,
cu.CompanyId,
cu.Position,
MIN(cu.SysStartTime) AS StartTime,
MAX(cu.SysEndTime) AS EndTime
FROM dbo.CompanyUser FOR SYSTEM_TIME ALL cu
GROUP BY cu.UserId, cu.CompanyId, cu.Position
Focusing on UserId 1, they were an 'Assistant', then a 'Manager', then back to an 'Assistant' again. I want to get the start and end date of each of these positions reguardless of how many Other changes are made between positions.
UserId CompanyId Position Other SysStartTime SysEndTime
-------- ----------- ----------- ------- ---------------------- ---------------------
1 1 Assistant A 2019-12-01 13:00:00 2019-12-01 14:00:00
2 1 Manager A 2019-12-01 13:00:00 2019-12-01 20:00:00
1 1 Assistant B 2019-12-01 14:00:00 2019-12-01 17:00:00
1 1 Manager A 2019-12-01 17:00:00 2019-12-01 20:00:00
2 1 Executive A 2019-12-01 20:00:00 9999-12-31 23:59:59
3 1 CEO A 2019-12-01 13:00:00 9999-12-31 23:59:59
1 1 Assistant A 2019-12-01 20:00:00 9999-12-31 23:59:59
I want a query that will return the following:
UserId CompanyId Position SysStartTime SysEndTime
-------- ----------- ----------- ---------------------- ---------------------
1 1 Assistant 2019-12-01 13:00:00 2019-12-01 17:00:00
2 1 Manager 2019-12-01 13:00:00 2019-12-01 20:00:00
1 1 Manager 2019-12-01 17:00:00 2019-12-01 20:00:00
2 1 Executive 2019-12-01 20:00:00 9999-12-31 23:59:59
3 1 CEO 2019-12-01 13:00:00 9999-12-31 23:59:59
1 1 Assistant 2019-12-01 20:00:00 9999-12-31 23:59:59
Thanks
This should do what you need (Fiddle).
WITH T
AS (SELECT *,
LAG(Position) OVER (PARTITION BY UserId ORDER BY SysStartTime) AS PrevPosition
FROM dbo.CompanyUser FOR SYSTEM_TIME ALL cu)
SELECT UserId,
CompanyId,
Position,
Other,
SysStartTime,
SysEndTime = LEAD(SysStartTime, 1, SysEndTime) OVER (PARTITION BY UserId ORDER BY SysStartTime)
FROM T
WHERE EXISTS (SELECT PrevPosition
EXCEPT
SELECT Position)
ORDER BY UserId,
SysStartTime
You should use LAG to achieve this.
SELECT UserId, CompanyId, Position, StartTime, EndTime
FROM
(
SELECT DISTINCT
cu.UserId,
cu.CompanyId,
cu.Position,
LAG(cu.Position) OVER(PARTITION BY cu.UserId,cu.Position ORDER BY (SELECT NULL)) NextPosition
MIN(cu.SysStartTime) AS StartTime,
MAX(cu.SysEndTime) AS EndTime
FROM dbo.CompanyUser FOR SYSTEM_TIME ALL cu
GROUP BY cu.UserId, cu.CompanyId, cu.Position
)T
WHERE Position <> ISNULL(NextPosition,'')
Result
UserId CompanyId Position SysStartTime SysEndTime
-------- ----------- ----------- ---------------------- ---------------------
1 1 Assistant 2019-12-01 13:00:00 2019-12-01 17:00:00
2 1 Manager 2019-12-01 13:00:00 2019-12-01 20:00:00
1 1 Manager 2019-12-01 17:00:00 2019-12-01 20:00:00
2 1 Executive 2019-12-01 20:00:00 9999-12-31 23:59:59
3 1 CEO 2019-12-01 13:00:00 9999-12-31 23:59:59
1 1 Assistant 2019-12-01 20:00:00 9999-12-31 23:59:59

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

Splitting time range based on activities in sql

I need a Oracle sql to show the following output given the sample input.
Basically, an employee is schedule for a 9 hour shift.
I need to split up the activities during the day to separate records.
Especially the general activity of Cash. I need to create new records.
Activity start time end time
Shift 2010-01-01 8:00:00 2010-01-01 17:00:00
Open 2010-01-01 8:00:00 2010-01-01 9:00:00
Cash 2010-01-01 9:00:00 2010-01-01 16:00:00
Break 2010-01-01 10:00:00 2010-01-01 10:15:00
Lunch 2010-01-01 12:00:00 2010-01-01 13:00:00
Break 2010-01-01 14:30:00 2010-01-01 14:45:00
Close 2010-01-01 16:00:00 2010-01-01 17:00:00
OUTPUT:
Activity start time end time
Open 2010-01-01 8:00:00 2010-01-01 9:00:00
Cash 2010-01-01 9:00:00 2010-01-01 10:00:00
Break 2010-01-01 10:00:00 2010-01-01 10:15:00
Cash 2010-01-01 10:15:00 2010-01-01 12:00:00
Lunch 2010-01-01 12:00:00 2010-01-01 13:00:00
Cash 2010-01-01 13:00:00 2010-01-01 14:30:00
Break 2010-01-01 14:30:00 2010-01-01 14:45:00
Cash 2010-01-01 14:45:00 2010-01-01 16:00:00
Close 2010-01-01 16:00:00 2010-01-01 17:00:00
Any help is greatly appreciated.
This is a kind of gaps-and-islands problem. Assuming there is always an 'Open' record whose start matches the 'Shift' start, and a 'Close' record whose end matches the 'Shift' end; and that the general activity is always 'Cash' and its start matches the 'Open' end and its end matches the 'Close' start; then some of those records are redundant when filling in the gaps.
You can use the lead and lag functions to generate dummy 'Cash' records that sit between all the other activities, looking both forward and behind:
select activity orig_activity, start_time orig_start, end_time orig_end,
'Cash' as activity, lag(end_time) over (order by end_time) as start_time, start_time as end_time
from table1
where activity not in ('Shift', 'Cash')
union all
select activity orig_activity, start_time orig_start, end_time orig_end,
'Cash' as activity, end_time as start_time, lead(start_time) over (order by start_time) as end_time
from table1
where activity not in ('Shift', 'Cash')
order by orig_start;
ORIG_ ORIG_START ORIG_END ACTI START_TIME END_TIME
----- ------------------- ------------------- ---- ------------------- -------------------
Open 2010-01-01 08:00:00 2010-01-01 09:00:00 Cash 2010-01-01 08:00:00
Open 2010-01-01 08:00:00 2010-01-01 09:00:00 Cash 2010-01-01 09:00:00 2010-01-01 10:00:00
Break 2010-01-01 10:00:00 2010-01-01 10:15:00 Cash 2010-01-01 09:00:00 2010-01-01 10:00:00
Break 2010-01-01 10:00:00 2010-01-01 10:15:00 Cash 2010-01-01 10:15:00 2010-01-01 12:00:00
Lunch 2010-01-01 12:00:00 2010-01-01 13:00:00 Cash 2010-01-01 10:15:00 2010-01-01 12:00:00
Lunch 2010-01-01 12:00:00 2010-01-01 13:00:00 Cash 2010-01-01 13:00:00 2010-01-01 14:30:00
Break 2010-01-01 14:30:00 2010-01-01 14:45:00 Cash 2010-01-01 14:45:00 2010-01-01 16:00:00
Break 2010-01-01 14:30:00 2010-01-01 14:45:00 Cash 2010-01-01 13:00:00 2010-01-01 14:30:00
Close 2010-01-01 16:00:00 2010-01-01 17:00:00 Cash 2010-01-01 17:00:00
Close 2010-01-01 16:00:00 2010-01-01 17:00:00 Cash 2010-01-01 14:45:00 2010-01-01 16:00:00
That has duplicates from the same gap being seen, for instance, after the break and before lunch. By ignoring the original values you can remove those with distinct, or with union instead of union all. You can also exclude any generated rows with null start or end times, and any that overlap with other records - which could happen if two other activities were contiguous:
select activity, start_time, end_time from (
select 'Cash' as activity,
lag(end_time) over (order by end_time) as start_time,
start_time as end_time
from table1
where activity not in ('Shift', 'Cash')
union
select 'Cash' as activity,
end_time as start_time,
lead(start_time) over (order by start_time) as end_time
from table1
where activity not in ('Shift', 'Cash')
) tmp
where start_time is not null
and end_time is not null
and not exists (
select null from table1 where activity not in ('Shift', 'Cash') and (start_time = tmp.start_time or end_time = tmp.end_time)
)
order by start_time;
ACTI START_TIME END_TIME
---- ------------------- -------------------
Cash 2010-01-01 09:00:00 2010-01-01 10:00:00
Cash 2010-01-01 10:15:00 2010-01-01 12:00:00
Cash 2010-01-01 13:00:00 2010-01-01 14:30:00
Cash 2010-01-01 14:45:00 2010-01-01 16:00:00
You can then union that with all the original table rows, except the 'Cash' record:
...
union all
select activity, start_time, end_time
from table1
where activity not in ('Shift', 'Cash')
order by start_time;
ACTIV START_TIME END_TIME
----- ------------------- -------------------
Open 2010-01-01 08:00:00 2010-01-01 09:00:00
Cash 2010-01-01 09:00:00 2010-01-01 10:00:00
Break 2010-01-01 10:00:00 2010-01-01 10:15:00
Cash 2010-01-01 10:15:00 2010-01-01 12:00:00
Lunch 2010-01-01 12:00:00 2010-01-01 13:00:00
Cash 2010-01-01 13:00:00 2010-01-01 14:30:00
Break 2010-01-01 14:30:00 2010-01-01 14:45:00
Cash 2010-01-01 14:45:00 2010-01-01 16:00:00
Close 2010-01-01 16:00:00 2010-01-01 17:00:00
This also assumes that activities never overlap, but non-'Cash' activities could be adjacent.
There are probably other gaps-and-islands approaches that would work too.
I'll second Alex's answer. But, just for something completely different, you could figure out the distinct seconds in the shift, figure out what the person was doing each second, then group those into ranges for your results.
I think this would be less efficient than Alex's approach, but might be more flexible: it doesn't assume as much about how the input data will look.
with shift_data ( activity, start_time, end_time ) AS
-- This is just test data that would be in your database table
(
SELECT 'Shift',to_date('2010-01-01 8:00:00','YYYY-MM-DD HH24:MI:SS'),to_date('2010-01-01 17:00:00','YYYY-MM-DD HH24:MI:SS') FROM DUAL UNION ALL
SELECT 'Open',to_date('2010-01-01 8:00:00','YYYY-MM-DD HH24:MI:SS'),to_date('2010-01-01 9:00:00','YYYY-MM-DD HH24:MI:SS') FROM DUAL UNION ALL
SELECT 'Cash',to_date('2010-01-01 9:00:00','YYYY-MM-DD HH24:MI:SS'),to_date('2010-01-01 16:00:00','YYYY-MM-DD HH24:MI:SS') FROM DUAL UNION ALL
SELECT 'Break',to_date('2010-01-01 10:00:00','YYYY-MM-DD HH24:MI:SS'),to_date('2010-01-01 10:15:00','YYYY-MM-DD HH24:MI:SS') FROM DUAL UNION ALL
SELECT 'Lunch',to_date('2010-01-01 12:00:00','YYYY-MM-DD HH24:MI:SS'),to_date('2010-01-01 13:00:00','YYYY-MM-DD HH24:MI:SS') FROM DUAL UNION ALL
SELECT 'Break',to_date('2010-01-01 14:30:00','YYYY-MM-DD HH24:MI:SS'),to_date('2010-01-01 14:45:00','YYYY-MM-DD HH24:MI:SS') FROM DUAL UNION ALL
SELECT 'Close',to_date('2010-01-01 16:00:00','YYYY-MM-DD HH24:MI:SS'),to_date('2010-01-01 17:00:00','YYYY-MM-DD HH24:MI:SS') FROM DUAL
),
seconds_in_shift as (
-- Step 1: get a list of every second that falls in the shift
SELECT start_time + (ROWNUM - 1) / 86400 second
FROM shift_data
WHERE activity = 'Shift'
CONNECT BY ROWNUM <= ( (end_time - start_time) * 86400) + 1),
activity_each_second as (
-- Step 2: figure out what the person was doing every second. If multiple
-- activities overlap, choose whichever one had the shortest duration
-- Also, mark which seconds represent a transition from one activity to
-- another ("marker" column)
SELECT second,
MAX (activity) KEEP (DENSE_RANK FIRST ORDER BY end_time - start_time) activity,
CASE WHEN MAX (activity) KEEP (DENSE_RANK FIRST ORDER BY end_time - start_time)
!= NVL(LAG(MAX (activity) KEEP (DENSE_RANK FIRST ORDER BY end_time - start_time))
OVER ( PARTITION BY NULL ORDER BY SECOND),'#NULL#') THEN 'Y' ELSE NULL END marker
FROM seconds_in_shift ss
INNER JOIN shift_data sd ON ss.second BETWEEN sd.start_time AND sd.end_time
GROUP BY second),
ranges as (
-- Step 3: count the number of marker columns from the beginning of the shift
-- to the current second. Call this "activity_number".
select aes.*,
count(marker) OVER ( PARTITION BY NULL ORDER BY second) activity_number
from activity_each_second aes )
-- Finally, show the activity, start, and end time for each activity_number
SELECT activity,
round(min(second),'MI') start_time,
round(max(second),'MI') end_time
FROM ranges
GROUP BY activity, activity_number
ORDER BY activity_number;
Results:
Open 1/1/2010 8:00:00 A 1/1/2010 9:00:00 AM
Cash 1/1/2010 9:00:00 A 1/1/2010 10:00:00 AM
Break 1/1/2010 10:00:00 1/1/2010 10:15:00 AM
Cash 1/1/2010 10:15:00 1/1/2010 12:00:00 PM
Lunch 1/1/2010 12:00:00 1/1/2010 1:00:00 PM
Cash 1/1/2010 1:00:00 P 1/1/2010 2:30:00 PM
Break 1/1/2010 2:30:00 P 1/1/2010 2:45:00 PM
Cash 1/1/2010 2:45:00 P 1/1/2010 4:00:00 PM
Close 1/1/2010 4:00:00 P 1/1/2010 5:00:00 PM
NOTE: I cheated a bit by rounding the times to the nearest minute. Without rounding, there would be overlap in the ranges. E.g., 4PM on-the-dot would either be "Cash" or "Close", it wouldn't be both.
Assumptions: All activity intervals fall within the 'Shift' interval, and two activity intervals may have at most an endpoint in common (they may be adjacent - but they can't overlap in any way).
Also, I assumed you may have more than one employee in your table (so that must be addressed), and that the computation must be done separately for each calendar day. You will see this in the input data, and handled in the query.
Here is a way to get the desired result using only the analytic lag() function. It first collects only the activities different from 'Shift' and 'Cash', then it fills the gaps with 'Cash' (including at the beginning and/or end of the 'Shift', if no specific activity, like 'Open' or 'Close', starts or ends at the beginning or the end of a 'Shift'). The 'Shift' interval in particular, as presented in the inputs, is not particularly helpful in this solution; you will see how I handle that in the CTE I called prep below.
So I don't need to enter nls_date_format everywhere, I first ran
alter session set nls_date_format = 'yyyy-mm-dd hh24:mi:ss'
Then:
with
table1 ( empno, activity, start_time, end_time ) as (
select 101, 'Shift', to_date('2010-01-01 8:00:00') , to_date('2010-01-01 17:00:00') from dual union all
select 101, 'Open' , to_date('2010-01-01 8:00:00') , to_date('2010-01-01 9:00:00') from dual union all
select 101, 'Cash' , to_date('2010-01-01 9:00:00') , to_date('2010-01-01 16:00:00') from dual union all
select 101, 'Break', to_date('2010-01-01 10:00:00'), to_date('2010-01-01 10:15:00') from dual union all
select 101, 'Lunch', to_date('2010-01-01 12:00:00'), to_date('2010-01-01 13:00:00') from dual union all
select 101, 'Break', to_date('2010-01-01 14:30:00'), to_date('2010-01-01 14:45:00') from dual union all
select 101, 'Close', to_date('2010-01-01 16:00:00'), to_date('2010-01-01 17:00:00') from dual
),
prep ( empno, activity, start_time, end_time, flag ) as (
select empno, activity, start_time, end_time, 1
from table1
where activity not in ('Shift', 'Cash')
union all select empno, 'Shift', start_time, start_time, 0
from table1
where activity = 'Shift'
union all select empno, 'Shift', end_time, end_time, 2
from table1
where activity = 'Shift'
),
with_cash_intervals ( empno, activity, start_time, end_time ) as (
select empno, activity, start_time, end_time
from prep
where activity != 'Shift'
union all
select empno, 'Cash', lag(end_time) over (partition by empno, trunc(start_time)
order by flag, start_time), start_time
from prep
)
select empno, activity, start_time, end_time
from with_cash_intervals
where start_time < end_time
order by empno, start_time -- if needed
Output:
EMPNO ACTIVITY START_TIME END_TIME
----- -------- ------------------- -------------------
101 Open 2010-01-01 08:00:00 2010-01-01 09:00:00
101 Cash 2010-01-01 09:00:00 2010-01-01 10:00:00
101 Break 2010-01-01 10:00:00 2010-01-01 10:15:00
101 Cash 2010-01-01 10:15:00 2010-01-01 12:00:00
101 Lunch 2010-01-01 12:00:00 2010-01-01 13:00:00
101 Cash 2010-01-01 13:00:00 2010-01-01 14:30:00
101 Break 2010-01-01 14:30:00 2010-01-01 14:45:00
101 Cash 2010-01-01 14:45:00 2010-01-01 16:00:00
101 Close 2010-01-01 16:00:00 2010-01-01 17:00:00
9 rows selected.

SQL Multiple record : Time Scheduler

I have problem about combining tables in store procedure.
Note : field "Time" is varchar
First table (tbTime)
Time
08:00:00
08:30:00
09:00:00
09:30:00
10:00:00
10:30:00
11:00:00
11:30:00
12:00:00
12:30:00
13:00:00
13:30:00
14:00:00
14:30:00
15:00:00
15:30:00
16:00:00
16:30:00
17:00:00
17:30:00
18:00:00
18:30:00
19:00:00
19:30:00
20:00:00
Second table (tbClassRsv)
select * from tbclassrsv where transdate='2014-02-05 00:00:00' and status<>'DEL'
transDate time until status studentCode tutor class description userID
2014-02-05 16:48:14 17:48:14 OPN ET-7201 ET-444 ROOM 01 try ADMIN
I want the result with condition schedule like this
Time Student
08:00:00 -
08:30:00 -
09:00:00 -
09:30:00 -
10:00:00 -
10:30:00 -
11:00:00 -
11:30:00 -
12:00:00 -
12:30:00 -
13:00:00 -
13:30:00 -
14:00:00 -
14:30:00 -
15:00:00 -
15:30:00 -
16:00:00 -
16:30:00 ET-7201
17:00:00 ET-7201
17:30:00 ET-7201
18:00:00 ET-7201
18:30:00 -
19:00:00 -
19:30:00 -
20:00:00 -
Thanks for reading or answer ^_^
GBU
I`ve tried this
select t.time,
isnull(
(select c.studentCode
from tbclassrsv c
where c.transdate='2014-02-05 00:00:00'
and c.class='ROOM 01'
and c.status<>'DEL'
and t.time>=c.time
and t.time<=c.until
),'-') [Student]
The result is....
Time Student
08:00:00 -
08:30:00 -
09:00:00 -
09:30:00 -
10:00:00 -
10:30:00 -
11:00:00 -
11:30:00 -
12:00:00 -
12:30:00 -
13:00:00 -
13:30:00 -
14:00:00 -
14:30:00 -
15:00:00 -
15:30:00 -
16:00:00 -
16:30:00 -
17:00:00 ET-7201
17:30:00 ET-7201
18:00:00 -
18:30:00 -
19:00:00 -
19:30:00 -
20:00:00 -
Try this. you were not converting your varchar times to datetime so that your time comparisons would work.
select t.time,
isnull(
(select c.studentCode
from tbClassRsv c
where c.transdate='2014-02-05 00:00:00'
and c.class='ROOM 01'
and c.status<>'DEL'
and DateAdd(MINUTE, 30, Convert(datetime, t.time))>= Convert(datetime, c.time)
and Convert(datetime, t.time) <= Convert(datetime, c.until)
),'-') from [tbTime] t
What you need to do is round c.time down to the nearest 30 minutes interval, and round c.until up to the nearest interval. This way your where clause will have the correct range.
To do the rounding you will need to convert the times to datetime which you can do like so:
CAST(CONVERT(varchar,THE_TIME_AS_VARCHAR,121) AS datetime)
Then you can round down to the nearest 30 minutes like so:
DATEADD(mi, DATEDIFF(mi, 0, THE_TIME_AS_DATETIME)/30*30, 0)
And round up like so:
DATEADD(mi, DATEDIFF(mi, 30, THE_TIME_AS_DATETIME)/30*30, 0)
Applying all that to your existing code would give you this:
select t.time,
isnull(
(select c.studentCode
from tbclassrsv c
where c.transdate='2014-02-05 00:00:00'
and c.class='ROOM 01'
and c.status<>'DEL'
and t.time>= DATEADD(mi, DATEDIFF(mi, 0, CAST(CONVERT(varchar,c.time,121) AS datetime))/30*30, 0)
and t.time<= DATEADD(mi, DATEDIFF(mi, 30, CAST(CONVERT(varchar,c.until,121) AS datetime))/30*30, 0)
),'-') [Student]