SQL how to split one row to multiple between two time? - sql

I have a row like:
EMPID INTIME OUTTIME JOBCODE
1 4:00 5:00 ABC
2 5:00 8:00 ABC
Expected Output:
EMPID TIMEID JOBCODE MINUTE
1 16 ABC 15
1 17 ABC 15
1 18 ABC 15
1 19 ABC 15
TIMEID FOR 4:00 is 16 and increase with 15 minutes threshold.
I have tried with below query but it generates a single row.
SELECT
TO_NUMBER(SUBSTR(TO_CHAR(INTIME,'HH24:MI:SS'),0,2)* 4) + ROUND(TO_NUMBER((SUBSTR(TO_CHAR(INTIME,'HH24:MI:SS'),4,2)))/15,0) AS TIMEID,
EMPID,
JOBCODE,
MINUTE FROM MYTABLE;

Split 1 row like that as below:
with
x as
(select 1 Empid, to_date('01-JAN-1900 '||'4:00','DD-MON-YYYY HH24:MI') intime, to_date('01-JAN-1900 '||'5:12','DD-MON-YYYY HH24:MI') outtime from dual)
Select Empid,intime,(newtime-nvl(lag(newtime) over (order by newtime),intime))*1440 Required_Intervals,outtime,newtime
from (
select EmpId,InTime,Case When Outtime > INTIME+(15*rownum/1440) Then INTIME+(15*rownum/1440) Else Outtime End newTime,OUTTIME
from x
connect by rownum <= CEIL(((outtime-intime)*1440)/15))
1440 = 24 (hours per day) * 60 (minutes per hour) = total minutes per day
Please note it will work for only 1 row. For multiple rows as well you can create a query using multiset & connect by level if required.
The query also accounts for when the out-time is not in intervals of 15.

Related

Can you pull a timestamp from a preceding record and check a condition?

I have a sample table below that shows a list of calls of each employee. Example: On Jan 1 Emp A took 4 calls, B took 4, etc. I am trying to find some anomalies within the start and end times for some calls. The end time for the Previous call cannot be more then the start time of the next call. Example: Call ID: 2 has a discrepancy. The call "ended" at 12:30 AM, but Emp A was on another call at 12:20 AM contradicting the call end time for call id 2. I am trying to write a script to flag these calls. The script will have to partition by Date first then partition by Emp then order by Time. Can this be done?
Call ID Date Emp StartTime EndTime
1 Jan 1 A 12:00 AM 12:10 AM
2 Jan 1 A 12:15 AM 12:30 AM
3 Jan 1 A 12:20 AM 12:45 AM
4 Jan 1 A 12:50 AM 1:00 AM
5 Jan 1 B 2:00 AM 2:30 AM
6 Jan 1 B 2:25 AM 2:50 AM
7 Jan 1 B 3:00 AM 3:50 AM
8 Jan 1 B 3:45 AM 4:00 AM
9 Jan 1 C 12:30 AM 12:45 AM
10 Jan 1 C 1:00 AM 1:30 AM
11 Jan 1 C 1:45 AM 1:50 AM
12 Jan 1 C 2:00 AM 2:10 AM
13 Jan 2 A ... ...
14 Jan 2 A ... ...
15 Jan 2 B ... ...
Result
Call ID Incorrect EndTime
2 12:30 AM
5 2:30 AM
7 3:50 AM
You can use exists to get overlaps:
select t.*
from t
where exists (select 1
from t t2
where t2.emp = t.emp and
t2.date = t.date and
t2.starttime < t.endtime and
t2.endtime > t.starttime
);
Note: This assume that no calls span midnight.
It seems you're looking to determine incorrect overlap based on LEAD(StartTime) versus EndTime. The PARTITION BY and ORDER BY for the LEAD function correspond to what's in the question.
with lead_cte as (
select *, lead(StartTime) over (partition by [Date], Emp
order by StartTime) lead_start
from calls)
select CallID, EndTime as [Incorrect EndTime]
from lead_cte
where EndTime>lead_start;
Use LEAD function.
SELECT
CallID,
Date,
Emp,
StartTime,
EndTime,
LEAD (StartTime) OVER (PARTITION BY Emp, Date ORDER BY StartTime) AS StartTimeNextCall, --this is for information
CASE
WHEN (LEAD (StartTime) OVER (PARTITION BY Emp, Date ORDER BY StartTime)) > EndTime
THEN 'discrepancy'
ELSE 'ok'
END AS Flag
FROM tab;
Obs.: I assume that column Date contains only the day, and not the time, otherwise the PARTITION BY clause must be different

SQL query to include time segments with no counts

I am working in SQL Server 2014. I have table that records 'counts' and a timestamp of the count. The counting period is a two hour block that can start at any quarter hour. In the example data below, the count starts at 16:00 and goes through 18:00. The counting block could have started at 01:30 and stopped at 03:30.
Timestamp Count
16:00:31 1
16:00:42 1
16:16:04 1
16:16:06 1
16:45:10 1
16:45:31 1
16:45:32 1
17:16:45 1
17:16:52 1
17:16:53 1
17:33:19 1
17:34:01 1
17:45:03 1
17:46:08 1
I have a query which sums the counts over 15 minute intervals within the two hour block:
SELECT
FORMAT(DATEPART(HOUR, [Timestamp]), '0#') + ':' + FORMAT(DATEPART(MINUTE, [TimeStamp]) / 15 * 15, '0#') AS QtrHrBeg
, COUNT(*) AS CountTotal
FROM
[Sandbox].[trippetoe].[SURVEYCOUNTS]
GROUP BY
DATEPART(HOUR, [TIMESTAMP])
, (DATEPART(MINUTE, [TIMESTAMP]) / 15 * 15)
which results in this:
QtrHrBeg Count
16:00 2
16:15 2
16:45 3
17:15 3
17:30 2
17:45 2
I'd like to include 15 minute intervals where there are no counts - in this example the quarter hours beginning at 16:30 and 17:00, like below:
QtrHrBeg Count
16:00 2
16:15 2
16:30 0
16:45 3
17:00 0
17:15 3
17:30 2
17:45 2
How can i do that?
See below.
Begin by creating a time table of all intervals for the day, then restricting that to the intervals for the 2 hour window you want.
Then left join that to the sum of your data table, pushing 0 where the join returns null.
DECLARE #Data TABLE ([TimeStamp] TIME, [Count] INT)
INSERT INTO #Data ([TimeStamp],[Count])
VALUES ('16:00:31',1),
('16:00:42',1),
('16:16:04',1),
('16:16:06',1),
('16:45:10',1),
('16:45:31',1),
('16:45:32',1),
('17:16:45',1),
('17:16:52',1),
('17:16:53',1),
('17:33:19',1),
('17:34:01',1),
('17:45:03',1),
('17:46:08',1)
;with AllIntervals AS
(
SELECT CONVERT(TIME,'00:00:00') AS Interval
UNION ALL
SELECT DATEADD(MINUTE,15,Interval)
FROM AllIntervals
WHERE Interval<'23:45:00'
), MyIntervals AS
(
SELECT CONVERT(VARCHAR(5),Interval,108) AS Interval
FROM AllIntervals
WHERE Interval >= (SELECT MIN(CONVERT(TIME,DATEADD(minute,(DATEDIFF(minute,0,[TimeStamp])/15)*15,0))) FROM #Data)
AND Interval < DATEADD(HOUR,2,(SELECT MIN(CONVERT(TIME,DATEADD(minute,(DATEDIFF(minute,0,[TimeStamp])/15)*15,0))) FROM #Data))
)
SELECT M.Interval, ISNULL(I.[Count],0)
FROM MyIntervals M
LEFT JOIN (SELECT CONVERT(TIME,DATEADD(minute,(DATEDIFF(minute,0,[TimeStamp])/15)*15,0)) AS Interval, SUM([Count]) AS Count
FROM #Data
GROUP BY CONVERT(TIME,DATEADD(minute,(DATEDIFF(minute,0,[TimeStamp])/15)*15,0))) I
ON M.Interval=I.Interval
You can use the following
Find the minimum date and the maximum date in the data you are going to work on , then round these two values to the nearest 15
Split the segment into 15 minutes intervals
Left join your data with the result came out and apply group by the StartTime and I used format in order to show the time formatting only
The benefit of this approach is that it works on specific interval and will not take any time interval outside of your data ranges.
with initial as(
select dateadd(minute, datediff(minute,0,min([Time])) / 15 * 15, 0) as MinTime,
dateadd(minute, datediff(minute,0,max([Time])) / 15 * 15, 0) as MaxTime
from data
), times as(
select StartTime = MinTime,
EndTime =dateadd(millisecond,-1,dateadd(minute,15,MinTime)),
MaxTime
from initial
union all
select dateadd(millisecond,1,EndTime),
dateadd(minute,15,EndTime),
MaxTime
from times
where EndTime<MaxTime
)
select format(t.StartTime,'HH:mm') as [Time],isnull(sum(d.[Count]),0) as [Count]
from times t
left join data d on d.[Time] between t.StartTime and t.EndTime
group by t.StartTime
Here is the output
Time Count
16:00 2
16:15 2
16:30 0
16:45 3
17:00 0
17:15 3
17:30 2
17:45 2
Here a working demo
Hope this will help you
EDIT
I changed the usage of second to millisecond based on the comment from #HABO, it will solve the case where there is some times like 16:59:59

SQL count number of users every 7 days

I am new to SQL and I need to find count of users every 7 days. I have a table with users for every single day starting from April 2015 up until now:
...
2015-05-16 00:00
2015-05-16 00:00
2015-05-17 00:00
2015-05-17 00:00
2015-05-17 00:00
2015-05-17 00:00
2015-05-17 00:00
2015-05-18 00:00
2015-05-18 00:00
...
and I need to count the number of users every 7 days (weekly) so I have data weekly.
SELECT COUNT(user_id), Activity_Date FROM TABLE_NAME
I need output like this:
TotalUsers week1 week2 week3 ..........and so on
82 80 14 16
I am using DB Visualizer to query Oracle database.
You should try following,
Select
sum(Week1) + sum(Week2) + sum(Week3) + sum(Week4) + sum(Week5) as Total,
sum(Week1) as Week1,
sum(Week2) as Week2,
sum(Week3) as Week3,
sum(Week4) as Week4,
sum(Week5) as Week5
From (
select
case when week = 1 then 1 else 0 end as Week1,
case when week = 2 then 1 else 0 end as Week2,
case when week = 3 then 1 else 0 end as Week3,
case when week = 4 then 1 else 0 end as Week4,
case when week = 5 then 1 else 0 end as Week5
from
(
Select
CEILING(datepart(dd,visitdate)/7+1) week,
user_id
from visitor
)T
)D
Here is Fiddle
You need to add month & year in the result as well.
SELECT COUNT(user_id), Activity_Date FROM TABLE_NAME WHERE Activity_Date > '2015-06-31';
That would get the amount of users for the last 7 days.
This is my test table:
user_id act_date
1 01/04/2015
2 01/04/2015
3 04/04/2015
4 05/04/2015
..
This is my query:
select week_offset, count(*) nb from (
select trunc((act_date-to_date('01042015','DDMMYYYY'))/7) as week_offset from test_date)
group by week_offset
order by 1
and this is the output:
week_offset nb
0 6
1 3
4 5
5 7
6 3
7 1
18 1
Week offset is the number of the week from 01/04/2015, and we can show the first day of the week.
See here for live testing.
How do you define your weeks? Here's an approach for SQL Server that starts each seven-day block relative to the start of April. The expressions will vary according to your specific needs:
select
dateadd(
dd,
datediff(dd, cast('20150401' as date), Activity_Date) / 7 * 7,
cast('20150401' as date)
) as WeekStart,
count(*)
from T
group by datediff(dd, cast('20150401' as date), Activity_Date) / 7
Oracle:
select
trunc(Activity_date, 'DAY') as WeekStart,
count(*)
from T
group by trunc(Activity_date, 'DAY') /* D and DAY are the same thing */

Select timestamps for these values SQL

Hello currently I have a working script below. I am using oracles 10
SELECT z.ID as "ID_One",
MAX(r.value) as "Max",
round(MAX(r.value)/80000,2) as "ROUND"
FROM Table1 r, Table2 z
WHERE r.timestamp > ((SYSDATE - TO_DATE('01/01/1970 00:00:00', 'MM-DD-YYYY HH24:MI:SS')) * 24 * 60 * 60) - 80000
AND r.id=21
AND r.animalid IN ('7','98','3','3')
AND r.id = z.id group by r.id, r.animalid,z.id;
It produces a table like this
ID_ONE MAX ROUND
1 500 232
2 232 32
3 23 .21
4 34 .321
I want to select a row call timestamp from table r. However when I add " r.timestamp " in to my query it produces 500 rows of data instead of 4. It looks like it is producing the the highest number for each timestamp instead. How would I produce a table that looks like this ? fyi timestamp column is in unix time. I can do the conversion myself. I just need to know how to get out these rows.
ID_ONE MAX ROUND TIMESTAMP
1 500 232 DEC 21,2021 10:00
2 232 32 DEC 21,2021 23:12
3 23 .21 DEC 31,2021 2:12
4 34 .321 DEC 31,2021 23:12
If you want other values from the row you're getting the MAX from, you can use analytic functions in a subquery instead of aggregates. There are various ways; this one ranks each row in the original tables such that the row with the highest value for each id is ranked first, and the outer query then only returns those.
SELECT id AS "ID_One",
value as "Max",
ROUND(value/80000,2) AS "ROUND",
timestamp,
TO_CHAR(DATE '1970-01-01' + (timestamp / (24 * 60 * 60)),
'MON DD,YYYY HH24:MI') AS timestamp_date
FROM (
SELECT z.id,
r.value,
r.timestamp,
RANK() OVER (PARTITION BY r.id, r.animalid ORDER BY r.value DESC) AS rnk
FROM Table1 r, Table2 z
WHERE r.timestamp > ((SYSDATE - DATE '1970-01-01') * 24 * 60 * 60) - 80000
--AND r.id=21
AND r.animalid IN ('7','98','3','3')
AND r.id = z.id
)
WHERE rnk = 1;
ID_One Max ROUND TIMESTAMP TIMESTAMP_DATE
---------- ---------- ---------- ---------- --------------------------
1 500 .01 1640080800 DEC 21,2021 10:00
2 232 0 1640128320 DEC 21,2021 23:12
3 23 0 1640916720 DEC 31,2021 02:12
4 34 0 1640992320 DEC 31,2021 23:12
SQL Fiddle, including the inner query on its own so you can see the ranking applied to each row.

SQL Server : Create Timetable

I have a table that records the start and end of a day schedule:
ScheduleID VenueID Date StartTime EndTime
1 1 14/07/2013 10:30 16:30
2 1 15/07/2013 09:30 16:30
3 2 16/07/2013 09:00 16:00
I need to create a table that self populates 30 minute time slots based in the start and end time so it looks something like this:
TimetableID VenueID Date Time_Slots Client1ID Client2ID
1 1 14/07/2013 10:30 45 Null
2 1 14/07/2013 11:00 67 78
3 1 14/07/2013 11:30 104 Null
4 1 14/07/2013 12:00 112 56
etc
Any help on how I generate the second table so that I can simple add the Client1ID and Clinet2ID would be greatly appreciated.
Thanks
Not sure what you mean be 'self populating table' and you don't say where the clientid's come from (so i'll leave that to you). But you can get a result set of 30 minute time slots with something like this
WITH cte AS
(
SELECT id, venue, schedule_date,
start_time time_slots, end_time
FROM schedule
UNION ALL
SELECT schedule.id, schedule.venue, schedule.schedule_date,
DATEADD(MINUTE, 30, cte.time_slots) time_slots,
schedule.end_time
FROM schedule
INNER JOIN cte
ON cte.id = schedule.id
AND cte.time_slots < schedule.end_time
)
SELECT ROW_NUMBER() OVER(ORDER BY id, time_slots),
venue,
schedule_date,
time_slots
FROM cte
ORDER BY id, time_slots
demo