SQL - Order by time, then text on beginning - sql

I have a query below :
SELECT DISTINCT TimeSched from tbl_schedule
It returns this result:
TimeSched
Rest Day
11:00 AM - 08:00 PM
No Schedule
09:00 AM - 06:00 PM
10:00 AM - 07:00 PM
When I use ORDER BY TimeSched, it returns this result:
TimeSched
09:00 AM - 06:00 PM
10:00 AM - 07:00 PM
11:00 AM - 08:00 PM
No Schedule
Rest Day
However, my desired result is I want 'Rest Day' and 'No Schedule' on the first and second row by default, then followed by the order of schedules in ascending order. As seen below :
TimeSched
Rest Day
No Schedule
09:00 AM - 06:00 PM
10:00 AM - 07:00 PM
11:00 AM - 08:00 PM

try this.
select * from timetable
order by
iif(timesched in ('No Schedule','Rest Day'),'01' + timesched,timesched)
Append a prefix for No Schedule and Restday with
01 when ordering.. so 01No will go 1, 01R will go next then your 09 to
24

How about simply doing:
SELECT DISTINCT TimeSched
FROM tbl_schedule
ORDER BY (CASE WHEN timesched LIKE '[a-zA-Z]%' THEN 1 ELSE 0 END),
timesched;

Try to make use of the below code :
DECLARE #TimeSched TABLE
(ID INT IDENTITY(1,1),TimeSched VARCHAR(20))
INSERT INTO #TimeSched
VALUES
('Rest Day'),
('11:00 AM - 08:00 PM'),
('No Schedule'),
('09:00 AM - 06:00 PM'),
('10:00 AM - 07:00 PM')
SELECT * FROM #TimeSched
ORDER BY
CASE
WHEN TimeSched ='Rest Day' THEN 1
WHEN TimeSched ='No Schedule' THEN 2
ELSE 3 END

Here is the answer to my question.
SELECT CASE TimeSched WHEN 'Restday' THEN 1 WHEN 'No Schedule' THEN 2 ELSE 3 END
AS TimeSked, TimeSched ORDER BY TimeSked, TimeSched

Related

How to create a sql statement or anonymous plsql block to increase count when only having a start time and an end time

I have an ask for a count of number of guests in a venue broken down to the minute. The data set I have available to me is the venue, the date/time the guest entered the venue, and the date/time the guest exited the venue. The business is asking for a breakdown by minute of the count of guests in the venue.
For example, guest A enters the venue at 12:00 and exits at 13:00. Guest B enters the venue at 12:30 and exits at 13:30. The expected output would show a count of 1 from 12:00 to 12:29, a count of two from 12:30 to 13:00, and back to a count of one from 13:00 to 13:30.
I’m struggling with the ask due to restrictions placed upon me. I am not authorized to make any structure changes; therefore, no DDL, which means I am restricted to SQL or anonymous PLSQL blocks. More information: however, I am unsure if it is necessary. The database version is 12.2c and it is running on AIX.
I do have a workaround where I extract the dataset as a csv and import it into a C# console application, which I wrote, but I would prefer if the ask can be conducted within the Oracle ecosystem.
I appreciate any help or insight you can share about my problem.
You can solve this problem with a combination of several tricks: connect by level <= 91 to create the 91 minutes for the time frame, a left join to include all minutes even if there isn't an event at that minute, a case and sum to count and sum arrivals and departures, and finally an analytic function to generate the running total of guests by adding arrivals and subtracting departures.
--The number of guests present per minute.
select
the_minute,
sum(arrive_counter + depart_counter) over (order by the_minute) guest_count
from
(
--Join time and visits and count arrivals and departures.
select
the_minute,
sum(case when the_minute = arrive_date then 1 else 0 end) arrive_counter,
sum(case when the_minute = depart_date then -1 else 0 end) depart_counter
from
(
--Every minute for a time period. (Change to 1441 for an entire day.)
select timestamp '2022-01-24 12:00:00' + (level - 1) * interval '1' minute the_minute
from dual
connect by level <= 91
) minutes
left join visit
on minutes.the_minute = arrive_date
or minutes.the_minute = depart_date
group by the_minute
order by the_minute
)
order by the_minute;
Results:
THE_MINUTE GUEST_COUNT
24-JAN-22 12.00.00.000000000 PM 1
24-JAN-22 12.01.00.000000000 PM 1
...
24-JAN-22 12.28.00.000000000 PM 1
24-JAN-22 12.29.00.000000000 PM 1
24-JAN-22 12.30.00.000000000 PM 2
24-JAN-22 12.31.00.000000000 PM 2
...
24-JAN-22 12.58.00.000000000 PM 2
24-JAN-22 12.59.00.000000000 PM 2
24-JAN-22 01.00.00.000000000 PM 1
24-JAN-22 01.01.00.000000000 PM 1
...
24-JAN-22 01.28.00.000000000 PM 1
24-JAN-22 01.29.00.000000000 PM 1
24-JAN-22 01.30.00.000000000 PM 0
You can use:
SELECT timestamp AS time_from,
LEAD(timestamp) OVER(ORDER BY timestamp) AS time_to,
SUM(SUM(change_in_guests)) OVER (ORDER BY timestamp) AS guests
FROM guests
UNPIVOT(
timestamp FOR change_in_guests IN (
entry AS +1,
exit AS -1
)
)
GROUP BY timestamp;
Which, for the sample data:
CREATE TABLE guests (id, entry, exit) AS
SELECT 'A', DATE '2022-01-25' + INTERVAL '12:00' HOUR TO MINUTE, DATE '2022-01-25' + INTERVAL '13:00' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT 'B', DATE '2022-01-25' + INTERVAL '12:30' HOUR TO MINUTE, DATE '2022-01-25' + INTERVAL '13:30' HOUR TO MINUTE FROM DUAL;
Outputs:
TIME_FROM
TIME_TO
GUESTS
2022-01-25 12:00:00
2022-01-25 12:30:00
1
2022-01-25 12:30:00
2022-01-25 13:00:00
2
2022-01-25 13:00:00
2022-01-25 13:30:00
1
2022-01-25 13:30:00
null
0
If you want it minute-by-minute then:
WITH minutes (minute, time_to, guests) AS (
SELECT timestamp,
LEAD(timestamp) OVER(ORDER BY timestamp),
SUM(SUM(change_in_guests)) OVER (ORDER BY timestamp)
FROM guests
UNPIVOT(
timestamp FOR change_in_guests IN (
entry AS +1,
exit AS -1
)
)
GROUP BY timestamp
UNION ALL
SELECT minute + INTERVAL '1' MINUTE,
time_to,
guests
FROM minutes
WHERE minute + INTERVAL '1' MINUTE < time_to
)
SEARCH DEPTH FIRST BY minute SET order_rn
SELECT minute,
guests
FROM minutes;
Which outputs:
MINUTE
GUESTS
2022-01-25 12:00:00
1
2022-01-25 12:01:00
1
2022-01-25 12:02:00
1
...
...
2022-01-25 12:28:00
1
2022-01-25 12:29:00
1
2022-01-25 12:30:00
2
2022-01-25 12:31:00
2
...
...
2022-01-25 12:58:00
2
2022-01-25 12:59:00
2
2022-01-25 13:00:00
1
2022-01-25 13:01:00
1
...
...
2022-01-25 13:28:00
1
2022-01-25 13:29:00
1
2022-01-25 13:30:00
0
db<>fiddle here

How to split the time range into multiple rows

I want to split the date/time ranges into multiple rows by hour in SQL Server but have some issues. My current dataset looks like this:
EmployeeCode StartDateTime EndDateTime
843578 2017-05-14 8:30 AM 2017-05-14 11:36 PM
587123 2017-05-14 22:00 PM 2017-05-15 01:28 AM
And I want something like this as my result table. Note that I want to treat a block less than an hour as one independent row as well. (For example 8:30AM - 9:00AM as one row.)
EmployeeCode StartDateTime EndDateTime
843578 2017-05-14 8:30 AM 2017-05-14 9:00 PM
843578 2017-05-14 9:00 AM 2017-05-14 10:00 AM
843578 2017-05-14 10:00 AM 2017-05-14 11:00 AM
843578 2017-05-14 11:00 AM 2017-05-14 11:36 AM
587123 2017-05-14 22:00 PM 2017-05-14 23:00 PM
587123 2017-05-14 23:00 PM 2017-05-15 00:00 AM
587123 2017-05-15 00:00 AM 2017-05-15 01:00 AM
587123 2017-05-15 01:00 AM 2017-05-15 01:28 AM
My current code only splits the date/time range that is within the same day. For example, the time range for Employee 587123 stops the spliting at 22:00 - 23:00 and doesn't work for the time range in next day.
How do I update my code to capture data after midnight? (The last three rows in the sample result table.)
Here's my current code
SELECT YT.EmployeeCode,
CASE WHEN YT.StartDateTime > DT.StartDateTime THEN YT.StartDateTime ELSE DT.StartDateTime END AS StartDateTime,
CASE WHEN YT.EndDateTime < DT.EndDateTime THEN YT.EndDateTime ELSE DT.EndDateTime END AS StartDateTime
FROM (VALUES(843578,CONVERT(datetime2(0),'2017-05-14T08:30:00'),CONVERT(datetime2(0),'2017-05-14T15:36:00')),
(587123,CONVERT(datetime2(0),'2017-05-14T09:00:00'),CONVERT(datetime2(0),'2017-05-14T18:28:00')))YT(EmployeeCode,StartDateTime,EndDateTime)
CROSS APPLY (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23))T(I)
CROSS APPLY (VALUES(DATEADD(HOUR,T.I,CONVERT(time(0),'00:00:00')),DATEADD(HOUR,T.I+1,CONVERT(time(0),'00:00:00'))))V(StartTime,EndTime)
CROSS APPLY (VALUES(DATETIMEFROMPARTS(YEAR(YT.StartDateTime),MONTH(YT.StartDateTime),DAY(YT.StartDateTime),DATEPART(HOUR,V.StartTime),DATEPART(MINUTE,V.StartTime),0,0),
DATETIMEFROMPARTS(YEAR(YT.StartDateTime),MONTH(YT.StartDateTime),DAY(YT.StartDateTime),DATEPART(HOUR,V.EndTime),DATEPART(MINUTE,V.EndTime),0,0)))DT(StartDateTime,EndDateTime)
WHERE YT.StartDateTime <= DT.EndDateTime
AND YT.EndDateTime >= DT.StartDateTime;
The current code looks too complicated so if you know better way to do this, please let me know.
I'd appreciate any help on this.
Here is a recursive CTE solution:
with cte as (
select
employeecode,
startdatetime,
dateadd(hour, 1, datetimefromparts(year(startdatetime), month(startdatetime), day(startdatetime), datepart(hour, startdatetime), 0, 0, 0)) enddatetime
enddatetime maxdatetime
from mytable
union all
select employeecode, enddatetime, dateadd(hour, 1, enddatetime), maxdatetime
from cte
where enddatetime < maxdatetime
)
select employeecode, startdatetime,
case when enddatetime < maxdatetime then enddatetime else maxdatetime end as enddatetime
from cte
Basically, the anchor of the CTE performs computes the end of the first range, using datetimefrompart(). Then we iteratively generate the following ranges, until the maximum date time is reached. We can then display the results with the outer query, while adjusting the end date of the last range.
I would approach this using a recursive CTE:
with cte as (
select t.EmployeeCode, t.StartDateTime as startdt,
dateadd(hour, datepart(hour, t.startdatetime) + 1, convert(datetime, convert(date, t.StartDateTime))) as enddt,
t.endDateTime, 1 as lev
from t
union all
select cte.employeecode, enddt,
(case when dateadd(hour, 1, enddt) < enddatetime then dateadd(hour, 1, enddt) else enddatetime end),
enddatetime, lev + 1
from cte
where enddt < enddatetime
)
select *
from cte
order by employeecode, startdt;
Here is a db<>fiddle.
If you might have spans of more than 100 hours, then you need option (maxrecursion 0) for the query.

SQL Server 2012 - Using ROW_NUMBER() on DISTINCT [duplicate]

This question already has answers here:
sql query distinct with Row_Number
(8 answers)
Closed 5 years ago.
I have a query below.
SELECT DISTINCT
FORMAT(CAST(SchedTi AS DATETIME),'hh:mm tt') AS SchedTimeIn,
FORMAT(CAST(SchedTO AS DATETIME),'hh:mm tt') AS SchedTimeOut
FROM
tblemployee_schedule
ORDER BY
SchedTimeIn
It returns this resultset:
SchedTimeIn SchedTimeOut
01:00 AM 09:00 AM
01:00 AM 10:00 AM
01:00 AM 10:00 PM
01:15 AM 05:15 AM
01:15 AM 10:15 AM
01:30 AM 05:30 AM
01:30 PM 10:30 PM
01:45 AM 05:45 AM
My desired result is:
SchedTimeIn SchedTimeOut ROWNUM
01:00 AM 09:00 AM 1
01:00 AM 10:00 AM 2
01:00 AM 10:00 PM 3
01:15 AM 05:15 AM 4
01:15 AM 10:15 AM 5
01:30 AM 05:30 AM 6
01:30 PM 10:30 PM 7
01:45 AM 05:45 AM 8
I tried the following query:
SELECT DISTINCT
ROW_NUMBER() OVER(ORDER BY SchedTi),
FORMAT(CAST(SchedTi AS DATETIME),'hh:mm tt') AS SchedTimeIn,
FORMAT(CAST(SchedTO AS DATETIME),'hh:mm tt') AS SchedTimeOut
FROM
tblemployee_schedule
ORDER BY
SchedTimeIn
But it returns too many redundant rows and NULL values.
Thank you in advance.
Try the below one,
SELECT * ,ROW_NUMBER() OVER(ORDER BY SchedTimeIn) AS ROWNUM
FROM (
SELECT DISTINCT
FORMAT(CAST(SchedTi AS DATETIME),'hh:mm tt') AS SchedTimeIn,
FORMAT(CAST(SchedTO AS DATETIME),'hh:mm tt') AS SchedTimeOut
FROM tblemployee_schedule
) AS D
ORDER BY ROWNUM
In your query ROW_NUMBER() produce a new sequence number for each records , so the DISTINCT key will not wok for the same, that's why you are getting too many redundant records. So you can use the ROW_NUMBER() in an outer query to overcome this.
Main problem is you are asking only part of problem,
In firstplace using distinct,order by,row_number in same query seem wrong.
using only partition function is enough.
any way using your query,
select *
,ROW_NUMBER()over(order by SchedTimeIn)rn
FROM
(
SELECT DISTINCT
FORMAT(CAST(SchedTi AS DATETIME),'hh:mm tt') AS SchedTimeIn,
FORMAT(CAST(SchedTO AS DATETIME),'hh:mm tt') AS SchedTimeOut
FROM tblemployee_schedule
ORDER BY SchedTimeIn
)tbl

Get classroom available hours between date time range

I'm, using Oracle 11g and I have this problem. I couldn't come up with any ideas to solve it yet.
I have a table with occupied classrooms. What I need to find are the hours available between a datetime range. For example, I have rooms A, B and C, the table of occupied classrooms looks like this:
Classroom start end
A 10/10/2013 10:00 10/10/2013 11:30
B 10/10/2013 09:15 10/10/2013 10:45
B 10/10/2013 14:30 10/10/2013 16:00
What I need to get is something like this:
with date time range between '10/10/2013 07:00' and '10/10/2013 21:15'
Classroom avalailable_from available_to
A 10/10/2013 07:00 10/10/2013 10:00
A 10/10/2013 11:30 10/10/2013 21:15
B 10/10/2013 07:00 10/10/2013 09:15
B 10/10/2013 10:45 10/10/2013 14:30
B 10/10/2013 16:00 10/10/2013 21:15
C 10/10/2013 07:00 10/10/2013 21:15
Is there a way I can accomplish that with sql or pl/sql?
I was looking at a solution similar in concept at least to Wernfried's, but I think it's different enough to post as well. The start is the same idea, first generating the possible time slots, and assuming you're looking at 15-minute windows: I'm using CTEs because I think they're clearer than nested selects, particularly with this many levels.
with date_time_range as (
select to_date('10/10/2013 07:00', 'DD/MM/YYYY HH24:MI') as date_start,
to_date('10/10/2013 21:15', 'DD/MM/YYYY HH24:MI') as date_end
from dual
),
time_slots as (
select level as slot_num,
dtr.date_start + (level - 1) * interval '15' minute as slot_start,
dtr.date_start + level * interval '15' minute as slot_end
from date_time_range dtr
connect by level <= (dtr.date_end - dtr.date_start) * (24 * 4) -- 15-minutes
)
select * from time_slots;
This gives you the 57 15-minute slots between the start and end date you specified. The CTE for date_time_range isn't strictly necessary, you could put your dates straight into the time_slots conditions, but you'd have to repeat them and that then introduces a possible failure point (and means binding the same value multiple times, from JDBC or wherever).
Those slots can then be cross-joined to the list of classrooms, which I'm assuming are already in another table, which gives you 171 (3x57) combinations; and those can be compared with existing bookings - once those are eliminated you're left with the 153 15-minute slots that have no booking.
with date_time_range as (...),
time_slots as (...),
free_slots as (
select c.classroom, ts.slot_num, ts.slot_start, ts.slot_end,
lag(ts.slot_end) over (partition by c.classroom order by ts.slot_num)
as lag_end,
lead(ts.slot_start) over (partition by c.classroom order by ts.slot_num)
as lead_start
from time_slots ts
cross join classrooms c
left join occupied_classrooms oc on oc.classroom = c.classroom
and not (oc.occupied_end <= ts.slot_start
or oc.occupied_start >= ts.slot_end)
where oc.classroom is null
)
select * from free_slots;
But then you have to collapse those into contiguous ranges. There are various ways of doing that; here I'm peeking at the previous and next rows to decide if a particular value is the edge of a range:
with date_time_range as (...),
time_slots as (...),
free_slots as (...),
free_slots_extended as (
select fs.classroom, fs.slot_num,
case when fs.lag_end is null or fs.lag_end != fs.slot_start
then fs.slot_start end as slot_start,
case when fs.lead_start is null or fs.lead_start != fs.slot_end
then fs.slot_end end as slot_end
from free_slots fs
)
select * from free_slots_extended
where (fse.slot_start is not null or fse.slot_end is not null);
Now we're down to 12 rows. (The outer where clause eliminates all 141 of the 153 slots from the previous step which are mid-range, since we only care about the edges):
CLASSROOM SLOT_NUM SLOT_START SLOT_END
--------- ---------- ---------------- ----------------
A 1 2013-10-10 07:00
A 12 2013-10-10 10:00
A 19 2013-10-10 11:30
A 57 2013-10-10 21:15
B 1 2013-10-10 07:00
B 9 2013-10-10 09:15
B 16 2013-10-10 10:45
B 30 2013-10-10 14:30
B 37 2013-10-10 16:00
B 57 2013-10-10 21:15
C 1 2013-10-10 07:00
C 57 2013-10-10 21:15
So those represent the edges, but on separate rows, and a final step combines them:
...
select distinct fse.classroom,
nvl(fse.slot_start, lag(fse.slot_start)
over (partition by fse.classroom order by fse.slot_num)) as slot_start,
nvl(fse.slot_end, lead(fse.slot_end)
over (partition by fse.classroom order by fse.slot_num)) as slot_end
from free_slots_extended fse
where (fse.slot_start is not null or fse.slot_end is not null)
Or putting all that together:
with date_time_range as (
select to_date('10/10/2013 07:00', 'DD/MM/YYYY HH24:MI') as date_start,
to_date('10/10/2013 21:15', 'DD/MM/YYYY HH24:MI') as date_end
from dual
),
time_slots as (
select level as slot_num,
dtr.date_start + (level - 1) * interval '15' minute as slot_start,
dtr.date_start + level * interval '15' minute as slot_end
from date_time_range dtr
connect by level <= (dtr.date_end - dtr.date_start) * (24 * 4) -- 15-minutes
),
free_slots as (
select c.classroom, ts.slot_num, ts.slot_start, ts.slot_end,
lag(ts.slot_end) over (partition by c.classroom order by ts.slot_num)
as lag_end,
lead(ts.slot_start) over (partition by c.classroom order by ts.slot_num)
as lead_start
from time_slots ts
cross join classrooms c
left join occupied_classrooms oc on oc.classroom = c.classroom
and not (oc.occupied_end <= ts.slot_start
or oc.occupied_start >= ts.slot_end)
where oc.classroom is null
),
free_slots_extended as (
select fs.classroom, fs.slot_num,
case when fs.lag_end is null or fs.lag_end != fs.slot_start
then fs.slot_start end as slot_start,
case when fs.lead_start is null or fs.lead_start != fs.slot_end
then fs.slot_end end as slot_end
from free_slots fs
)
select distinct fse.classroom,
nvl(fse.slot_start, lag(fse.slot_start)
over (partition by fse.classroom order by fse.slot_num)) as slot_start,
nvl(fse.slot_end, lead(fse.slot_end)
over (partition by fse.classroom order by fse.slot_num)) as slot_end
from free_slots_extended fse
where (fse.slot_start is not null or fse.slot_end is not null)
order by 1, 2;
Which gives:
CLASSROOM SLOT_START SLOT_END
--------- ---------------- ----------------
A 2013-10-10 07:00 2013-10-10 10:00
A 2013-10-10 11:30 2013-10-10 21:15
B 2013-10-10 07:00 2013-10-10 09:15
B 2013-10-10 10:45 2013-10-10 14:30
B 2013-10-10 16:00 2013-10-10 21:15
C 2013-10-10 07:00 2013-10-10 21:15
SQL Fiddle.
It is always a challenge when you like to "select something which does not exist". First you need a list of all available classrooms and times (in interval of 15 Minutes). Then you can select them by skipping the occupied items.
I managed to make a query without any PL/SQL:
CREATE TABLE Table1
(Classroom VARCHAR2(10), start_ts DATE, end_ts DATE);
INSERT INTO Table1 VALUES ('A', TIMESTAMP '2013-01-10 10:00:00', TIMESTAMP '2013-01-10 11:30:00');
INSERT INTO Table1 VALUES ('B', TIMESTAMP '2013-01-10 09:15:00', TIMESTAMP '2013-01-10 10:45:00');
INSERT INTO Table1 VALUES ('B', TIMESTAMP '2013-01-10 14:30:00', TIMESTAMP '2013-01-10 16:00:00');
WITH all_rooms AS
(SELECT CHR(64+LEVEL) AS ROOM FROM dual CONNECT BY LEVEL <= 3),
all_times AS
(SELECT CAST(TIMESTAMP '2013-01-10 07:00:00' + (LEVEL-1) * INTERVAL '15' MINUTE AS DATE) AS TIMES, LEVEL AS SLOT
FROM DUAL
CONNECT BY TIMESTAMP '2013-01-10 07:00:00' + (LEVEL-1) * INTERVAL '15' MINUTE <= TIMESTAMP '2013-01-10 21:15:00'),
all_free_slots AS
(SELECT ROOM, TIMES, SLOT,
CASE SLOT-LAG(SLOT, 1, 0) OVER (PARTITION BY ROOM ORDER BY SLOT)
WHEN 1 THEN 0
ELSE 1
END AS NEW_WINDOW
FROM all_times
CROSS JOIN all_rooms
WHERE NOT EXISTS
(SELECT 1 FROM TABLE1 WHERE ROOM = CLASSROOM AND TIMES BETWEEN START_TS + INTERVAL '1' MINUTE AND END_TS - INTERVAL '1' MINUTE)),
free_time_windows AS
(SELECT ROOM, TIMES, SLOT,
SUM(NEW_WINDOW) OVER (PARTITION BY ROOM ORDER BY SLOT) AS WINDOW_ID
FROM all_free_slots)
SELECT ROOM,
TO_CHAR(MIN(TIMES), 'yyyy-mm-dd hh24:mi') AS free_time_start,
TO_CHAR(MAX(TIMES), 'yyyy-mm-dd hh24:mi') AS free_time_end
FROM free_time_windows
GROUP BY ROOM, WINDOW_ID
HAVING MAX(TIMES) - MIN(TIMES) > 0
ORDER BY ROOM, 2;
ROOM FREE_TIME_START FREE_TIME_END
---- ----------------------------------
A 2013-01-10 07:00 2013-01-10 10:00
A 2013-01-10 11:30 2013-01-10 21:15
B 2013-01-10 07:00 2013-01-10 09:15
B 2013-01-10 10:45 2013-01-10 14:30
B 2013-01-10 16:00 2013-01-10 21:15
C 2013-01-10 07:00 2013-01-10 21:15
In order to understand the query you can split the sub-queries from top, e.g.
WITH all_rooms AS
(SELECT CHR(64+LEVEL) AS ROOM FROM dual CONNECT BY LEVEL <= 3),
all_times AS
(SELECT CAST(TIMESTAMP '2013-01-10 07:00:00' + (LEVEL-1) * INTERVAL '15' MINUTE AS DATE) AS TIMES, LEVEL AS SLOT
FROM DUAL
CONNECT BY TIMESTAMP '2013-01-10 07:00:00' + (LEVEL-1) * INTERVAL '15' MINUTE <= TIMESTAMP '2013-01-10 21:15:00')
SELECT ROOM, TIMES, SLOT,
CASE SLOT-LAG(SLOT, 1, 0) OVER (PARTITION BY ROOM ORDER BY SLOT)
WHEN 1 THEN 0
ELSE 1
END AS NEW_WINDOW
FROM all_times
CROSS JOIN all_rooms
WHERE NOT EXISTS (SELECT 1 FROM TABLE1 WHERE ROOM = CLASSROOM AND TIMES BETWEEN START_TS + INTERVAL '1' MINUTE AND END_TS - INTERVAL '1' MINUTE)
ORDER BY ROOM, SLOT

SQL Server Split Day in 4 Blocks of 2 hours

I have few records of tasks with StartTime and StopTime and I would like to split it in 4 records of 2 hours (8:30 - 10:30 / 10:30 - 12:30 / 13:00 - 15:00 / 15:00 - 17:00)
Typically, i would like to split this row :
From
Title StartTime StopTime
Task1 2013-11-12 09:00 2013-11-12 14:00
To
Title StartTime StopTime
Task1 2013-11-12 09:30 2013-11-12 10:30
Task1 2013-11-12 10:30 2013-11-12 12:30
Task1 2013-11-12 12:30 2013-11-12 14:00
Any suggestion is welcome, thanks.
Try this query:
SQLFiddle demo
select [TaskName],
CASE WHEN CONVERT(varchar,StartTime,108)<pStart
THEN floor(CAST(StartTime as float))+Cast(pStart as Datetime)
ELSE StartTime
END as StartTime,
CASE WHEN CONVERT(varchar,StopTime,108)>pEnd
THEN floor(CAST(StopTime as float))+Cast(pEnd as Datetime)
ELSE StopTime
END as StopTime
FROM T
JOIN
(
SELECT '08:30:00' as pStart, '10:30:00' as pEnd
UNION ALL
SELECT '10:30:00' as pStart, '12:30:00' as pEnd
UNION ALL
SELECT '13:00:00' as pStart, '15:00:00' as pEnd
UNION ALL
SELECT '15:00:00' as pStart, '17:00:00' as pEnd
) as DayParts
ON (CONVERT(varchar,StartTime,108)<=pEnd
AND
CONVERT(varchar,StopTime,108)>=pEnd
)
OR
(
CONVERT(varchar,StopTime,108)>=pStart
AND
CONVERT(varchar,StartTime,108)<=pStart
)
ORDER BY TaskName,StartTime