calculate car parking hours in sql server - sql-server

I have one doubt in sql server. How to calculate car login and logout time details?
table: cardetails
Here need to calculate each car how many hours spend in parking area.
creatE TABLE [dbo].[CarDetails](
[carid] [int] NULL,
[DateTimeDetail] [datetime] NULL,
[Flag] [varchar](50) NULL
)
INSERT [dbo].[CarDetails] ([carid], [DateTimeDetail], [Flag]) VALUES (1, CAST(N'2019-01-20T19:05:00.000' AS DateTime), N'in')
GO
INSERT [dbo].[CarDetails] ([carid], [DateTimeDetail], [Flag]) VALUES (1, CAST(N'2019-01-20T22:30:00.000' AS DateTime), N'out')
GO
INSERT [dbo].[CarDetails] ([carid], [DateTimeDetail], [Flag]) VALUES (2, CAST(N'2019-01-20T20:30:10.000' AS DateTime), N'in')
GO
INSERT [dbo].[CarDetails] ([carid], [DateTimeDetail], [Flag]) VALUES (2, CAST(N'2019-01-21T02:10:10.000' AS DateTime), N'out')
GO
INSERT [dbo].[CarDetails] ([carid], [DateTimeDetail], [Flag]) VALUES (3, CAST(N'2019-01-23T07:07:40.000' AS DateTime), N'in')
GO
INSERT [dbo].[CarDetails] ([carid], [DateTimeDetail], [Flag]) VALUES (3, CAST(N'2019-01-23T10:50:40.000' AS DateTime), N'out')
GO
INSERT [dbo].[CarDetails] ([carid], [DateTimeDetail], [Flag]) VALUES (3, CAST(N'2019-01-23T11:00:10.000' AS DateTime), N'in')
GO
INSERT [dbo].[CarDetails] ([carid], [DateTimeDetail], [Flag]) VALUES (3, CAST(N'2019-01-23T14:15:30.000' AS DateTime), N'out')
GO
INSERT [dbo].[CarDetails] ([carid], [DateTimeDetail], [Flag]) VALUES (2, CAST(N'2019-01-21T08:20:10.000' AS DateTime), N'in')
GO
INSERT [dbo].[CarDetails] ([carid], [DateTimeDetail], [Flag]) VALUES (2, CAST(N'2019-01-21T10:20:10.000' AS DateTime), N'out')
GO
These are the records:
carid DateTimeDetail Flag
1 2019-01-20 19:05:00.000 in
1 2019-01-20 22:30:00.000 out
2 2019-01-20 20:30:10.000 in
2 2019-01-21 02:10:10.000 out
3 2019-01-23 07:07:40.000 in
3 2019-01-23 10:50:40.000 out
3 2019-01-23 11:00:10.000 in
3 2019-01-23 14:15:30.000 out
2 2019-01-21 08:20:10.000 in
2 2019-01-21 10:20:10.000 out
Based on above data I want output like below :
carid | DateTimeDetails | Totaltime(hh:mm:ss)
1 |2019-01-20 | 03:25:00
2 |2019-01-20 | 05 :49:40
2 |2019-01-21 | 02:00:00
3 |2019-01-23 | 06:58:20
I tried like below
select a.carid , sum(datediff(mm,b.datetimedetail,a.datetimedetail))as totalmm from CarDetails a join CarDetails b
on a.carid=b.carid
where a.datetimedetail<=(select max(c.[DateTimeDetail]) from CarDetails c join CarDetails a on a.carid=c.carid
)
group by a.carid
please tell me how to write query to achive this task in sql server

This doesn't quite give you the expected results in your post, however, I have good reason for it not to be, and I suspect your expected results are wrong:
--Because, from experience, people can somehow enter things twice before exiting...
WITH Grp AS(
SELECT CD.carid,
CD.DateTimeDetail,
CD.Flag,
COUNT(CASE Flag WHEN 'Out' THEN 1 END) OVER (PARTITION BY carid ORDER BY CD.DateTimeDetail ASC
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS Grp
FROM dbo.CarDetails CD),
InOut AS(
SELECT carid,
MIN(CASE Flag WHEN 'In' THEN Grp.DateTimeDetail END) AS TimeIn,
MAX(CASE Flag WHEN 'Out' THEN Grp.DateTimeDetail END) AS [TimeOut]
FROM Grp
GROUP BY carid, grp)
SELECT carid,
CONVERT(date,TimeIn) AS DateTimeDetails,
CONVERT(time(0),DATEADD(SECOND,SUM(DATEDIFF(SECOND,TimeIn, [TimeOut])),'00:00:00')) AS TotalTime
FROM InOut
GROUP BY carid,
CONVERT(date,TimeIn)
ORDER BY carid ASC;
This gives the results below:
carid DateTimeDetails TotalTime
----------- --------------- ----------------
1 2019-01-20 03:25:00
2 2019-01-20 05:40:00
2 2019-01-21 02:00:00
3 2019-01-23 06:58:20
Notice I have a 05:40:00 for car 2 on 2019-01-20. Car 2 enters at 20:30:10 and leaves at 02:10:10, which is 5 hours, and 40 minutes later; not 05 hours 49 minutes and 40 seconds later. if you have that time in your expected results for a reason, you need to explain why.
Note: This will not work if a car stays more than 24 hours! You didn't respond to my question, so I have assumed not. If they can, SQL Server does not support times great than 24 hours, therefore you will be better returning the number of seconds, and having your application display a 24+ hour time.

you can try this
select carid,dateIn, cast(DATEADD(ms, SUM(DATEDIFF(ms, '00:00:00.000', timeout)), '00:00:00.000') as time) timeOut
from (
select carid,cast([DateTimeDetail]as DATE) DateIn ,
(select top 1 [DateTimeDetail]
from [CarDetails] b
where b.carid=a.carid and b.[DateTimeDetail]>a.[DateTimeDetail] and flag='out' )
-[DateTimeDetail] timeOut
from [CarDetails] a
where Flag='in') c
group by carid,dateIn
This gives the results below:
carid dateIn timeOut
1 2019-01-20 03:25:00.0000000
2 2019-01-20 05:40:00.0000000
2 2019-01-21 02:00:00.0000000
3 2019-01-23 06:58:20.0000000

Related

Splitting a Date from hours and mins timestamp

In snowflake, I have a date time stamp. '2022-07-18 08:00:00"
How do I separate the day from the time? I want to group by the day, but cant because of the time.
Thank you.
In Snowflake you can use the DAY or DATE functions, e.g.
create a test table
create or replace table table_with_dates (ID number, DATE timestamp);
insert values
insert into table_with_dates values (1, '2022-07-18 08:00:00'),
(2, '2022-07-18 08:00:00'),
(3, '2022-07-18 08:00:00'),
(1, '2022-07-19 08:00:00'),
(2, '2022-07-19 08:00:00'),
(1, '2022-07-20 08:00:00'),
(2, '2022-07-20 08:00:00'),
(1, '2022-07-21 08:00:00');
select the data grouping by the DATE part
select date(DATE), count(*) from table_with_dates
group by date(DATE);
select the date grouping by the DAY part
select DAY(DATE), count(*) from table_with_dates
group by DAY(DATE);
So the simplest way is to cast to DATE
some data in a CTE so work against:
with data(timestamp) as (
select column1::timestamp
from values
('2022-07-18 08:00:00'),
('2022-07-18 08:00:00'),
('2022-07-18 08:00:00'),
('2022-07-19 08:00:00'),
('2022-07-19 08:00:00'),
('2022-07-20 08:00:00'),
('2022-07-20 08:00:00'),
('2022-07-21 08:00:00')
)
select
d.timestamp::date as date
,count(*) as count
from data as d
group by 1
order by 1;
gives:
DATE
COUNT
2022-07-18
3
2022-07-19
2
2022-07-20
2
2022-07-21
1
DATE_TRUNC with DAY gives the same results, but is a little verbose.
select
date_trunc('day', d.timestamp::date) as date
,count(*) as count
from data as d
group by 1
order by 1;
DATE
COUNT
2022-07-18
3
2022-07-19
2
2022-07-20
2
2022-07-21
1

Time Duration for staff on a night shift with onguard system accesscontrol database

I am using the Lenel Onguard with SQL server dBase to make the time attendance system for our employees.
I summarize each day's transaction to make their first entry and last exit for each day and get the datediff. to get the time duration. But the problem is with night shift employees, it is showing the time out which happened in the morning of the same day where actual exit is on the next day. so the datediff. returning with wrong value. Any solutions are most welcomed!
This code gives me the wrong value for night shift.can anyone help me out to modify the code to fit staff on day shift and those on night shift in which their exit time is on next day
SELECT DISTINCT
BADGE.ID,
UPPER(ISNULL(dbo.EMP.FIRSTNAME, ' ') + ' ' + ISNULL(dbo.EMP.LASTNAME, ' ') + ' ' + ISNULL(dbo.EMP.MIDNAME, ' '))AS NAMES,
A.*,
B.TIMEOUT,
datediff(hour,a.[TIMEIN],b.TIMEOUT) HoursWorked
FROM (
SELECT empid,convert(date,event_time_utc)[Date],ltrim(right(convert(varchar(25), DATEADD(HOUR,3,CAST(min(event_time_utc)AS TIME)), 100), 7)) TIMEIN
FROM events INNER JOIN READER ON EVENTS.DEVID=READER.READERID INNER JOIN EVENT ON EVENTS.EVENTTYPE=EVENT.EVTYPEID AND EVENTS.EVENTID=EVENT.EVID
WHERE READERID=19 AND PANELID=16 AND EVDESCR='Access Granted'
GROUP BY empid,convert(date,event_time_utc)
) A
JOIN
(
SELECT empid,convert(date,event_time_utc)[Date],ltrim(right(convert(varchar(25), DATEADD(HOUR,3,CAST(MAX(event_time_utc)AS TIME)), 100), 7)) TIMEOUT
FROM events INNER JOIN READER ON EVENTS.DEVID=READER.READERID INNER JOIN EVENT ON EVENTS.EVENTTYPE=EVENT.EVTYPEID AND EVENTS.EVENTID=EVENT.EVID
WHERE READERID=20 AND PANELID=16 AND EVDESCR='Access Granted'
GROUP BY empid,convert(date,event_time_utc)
) B on A.empid=b.empid and a.[Date]=b.[Date]
JOIN Emp on emp.id=A.EmpID
JOIN BADGE ON BADGE.EMPID=A.EMPID
ORDER BY DATE
Results
EmpID TIMEIN Timeout
1 2014-08-21 21:38:06.000 2014-08-22 06:00:10.000
2 2014-08-22 22:30:00.000 2014-08-23 06:00:10.000
In summary you have Event/s table, that have EmpId, the datetime of each scan, and there is no info about TimeOut or TimeIn, and you want to find that, well from the information you provided its a bit hard, Im sure you have more data that will help make this simpler, like the shift hours, any limitations, is over hours accepted and more boundary information.
Lets say you dont, so we will need to put some assumptions, for example, as the employees work in shifts, I will assume that the shift is 8-10 hours, and if I see a gab in scanning more than 14 hours that the employee went home, I will not care if he is a night shift employee or a day shift, if there is a gab between two sequentialscans more than 14 hours that mean he went home, or in other words that is a TimeOut, and then next entry is a TimeIn.
You did not provide any table structure or data, so I will ignore your query and focus on the problem you want to solve, so I will assume I have only one Events Table that have all the data, you can adapt this to your query if this helps you.
I will create a memory table here and fill it with EmpId 1, that is a on the day shift and EmpId 2 that is on the night shift, I will assume some data and do some calculations.
Declare #Events TABLE(
EmpId int,
event_time_utc datetime
)
insert into #Events values
(1, '2014-08-21 07:38:06.000'),--first day for emp1
(1, '2014-08-21 08:39:06.000'),
(1, '2014-08-21 14:44:06.000'),
(1, '2014-08-21 15:38:06.000'),
(1, '2014-08-21 16:01:06.000'),
(1, '2014-08-22 07:40:06.000'),--second day for emp1
(1, '2014-08-22 08:50:06.000'),
(1, '2014-08-22 14:30:06.000'),
(1, '2014-08-22 15:30:06.000'),
(1, '2014-08-22 16:05:06.000'),
(1, '2014-08-23 07:38:06.000'),--3rd day for emp1
(1, '2014-08-23 08:39:06.000'),
(1, '2014-08-23 14:44:06.000'),
(1, '2014-08-23 15:38:06.000'),
(1, '2014-08-23 16:01:06.000'),
(1, '2014-08-24 07:40:06.000'),--4th day for emp1
(1, '2014-08-24 08:50:06.000'),
(1, '2014-08-24 14:30:06.000'),
(1, '2014-08-24 15:30:06.000'),
(1, '2014-08-24 16:05:06.000'),
(2, '2014-08-21 21:38:06.000'),--first day for emp2 -- night shift
(2, '2014-08-21 23:38:06.000'),
(2, '2014-08-22 01:38:06.000'),
(2, '2014-08-22 04:05:06.000'),
(2, '2014-08-22 21:38:06.000'),--first day for emp2 -- night shift
(2, '2014-08-22 23:38:06.000'),
(2, '2014-08-23 01:38:06.000'),
(2, '2014-08-23 04:05:06.000'),
(2, '2014-08-23 21:38:06.000'),--3rd day for emp2 -- night shift
(2, '2014-08-23 23:38:06.000'),
(2, '2014-08-24 01:38:06.000'),
(2, '2014-08-24 04:05:06.000'),
(2, '2014-08-24 21:38:06.000'),--4th day for emp2 -- night shift
(2, '2014-08-24 23:38:06.000'),
(2, '2014-08-25 01:38:06.000'),
(2, '2014-08-25 04:05:06.000')
Now I will use the below CTEs to figure out the TimeIn and TimeOut and calculate the hours.
;with cte as (
--get the next entry, and set a row number based on EmpID and time
select *
,LEAD(event_time_utc,1) over (partition by EmpId order by event_time_utc) nextEntry
,ROW_NUMBER() over (partition by EmpId order by event_time_utc) seq
from #Events
),cte2 as (
--count the hours between this entry and the one after
select *,datediff(hour,event_time_utc,nextEntry) [hours] from cte
),cte3 as (
--if gab more then 14 or if its null, set it as time in
select *
,case when seq=1 then event_time_utc
when [hours]>14 then nextEntry
else null end [TimeIn]
from cte2
),cte4 as (
--find the seq for the Timeout
select *,
Isnull(
lead(seq) over (partition by EmpId order by event_time_utc)
,(select top(1) cte.seq from cte where cte.EmpId=cte3.EmpId order by event_time_utc desc)) [TimeOutSeq]
from cte3 where TimeIn is not null
),cte5 as (
--convert the seq to timeout by joining to the same table using the TimeOutSeq to help
select cte4.*,cte3.event_time_utc [TimeOut] from cte4
left outer join cte3 on cte3.seq=cte4.TimeOutSeq and cte3.EmpId=cte4.EmpId
)
--select * from cte3
--finally show the needed fileds only, and the hours for each employee
select EmpId,seq,TimeIn,[TimeOut],datediff(Hour,TimeIn,[TimeOut]) [hours] from cte5 order by EmpId, TimeIn
The result for my set of data is as below:-
EmpId Seq TimeIn TimeOut hours
1 1 2014-08-21 07:38:06.000 2014-08-21 16:01:06.000 9
1 5 2014-08-22 07:40:06.000 2014-08-22 16:05:06.000 9
1 10 2014-08-23 07:38:06.000 2014-08-23 16:01:06.000 9
1 15 2014-08-24 07:40:06.000 2014-08-24 16:05:06.000 9
2 1 2014-08-21 21:38:06.000 2014-08-22 04:05:06.000 7
2 4 2014-08-22 21:38:06.000 2014-08-23 04:05:06.000 7
2 8 2014-08-23 21:38:06.000 2014-08-24 04:05:06.000 7
2 12 2014-08-24 21:38:06.000 2014-08-25 04:05:06.000 7

How to re write while loop using cte

I have two tables, one with Events, the other with episodes.
An Episode has a start date and end date, the event has a single date.
Both Episodes and Events have one of six Types.
Currently I'm using some fuzzy logic to run an update script on the Events table to set it's ID field to the matching Episode. It does this by checking for the Event date between the Episode start and end, both having the same Type, as well as some other links like same User etc.
Since the Events can sit outside of the Episode, or have a different Type, what I do is loop through a sequence of expanding date ranges (StartDate-1, -2 etc) and also cycle through each Type looking for a match.
I've been reading that while loops aren't very efficient, so was wondering if there was a way to rewrite this nested loop into a CTE function.
I'm using SQL Server 2012.
Event List is just a temp table that has all the possible Types with an order to loop through.
My loop currently is:
WHILE #CurrBefore <= #Before and #CurrentAfter <= #After
BEGIN
SET #Row = 0
WHILE #Row <= #MaxRow
BEGIN
UPDATE P
SET P.ID = E.ID
FROM Event P
OUTER APPLY (SELECT TOP 1 E.Id, E.Type
FROM Episode E
WHERE E.User = P.User AND
E.Type = CASE WHEN #Row=0 THEN P.Event ELSE (SELECT Event FROM #EventList WHERE RN = #Row) END AND
P.Date BETWEEN E.StartDate-#CurrentBefore AND E.EndDate+#CurrentAfter
ORDER BY P.Date) E
WHERE P.ID = 0
INCREMENT #ROW CODE
END
INCREMENT #BEFORE/AFTER CODE
END
Sample Data:
IF OBJECT_ID('tempdb..#EventList') IS NOT NULL
BEGIN
DROP TABLE #EventList
CREATE TABLE #EventList(Event Varchar(50), RN INT);
INSERT INTO #EventList SELECT 'A', 1
INSERT INTO #EventList SELECT 'B', 2
INSERT INTO #EventList SELECT 'C', 3
INSERT INTO #EventList SELECT 'D', 4
INSERT INTO #EventList SELECT 'E', 5
INSERT INTO #EventList SELECT 'F', 6
END
CREATE TABLE dbo.Episode ([ID] INT, [Start] DateTime, [End] DateTime, [Type] varchar(1), [User] INT)
INSERT INTO [dbo].Episode ([ID], [Start], [End], [Type],[User])
VALUES
(1, '2018-07-01 10:00', '2018-07-02 14:00', 'A',10),
(2, '2018-07-05 6:00', '2018-07-06 13:00', 'A',11),
(3, '2018-07-03 9:00', '2018-07-04 8:00', 'B',10),
(4, '2018-07-02 15:00', '2018-07-03 7:00', 'B',12),
(5, '2018-07-01 1:00', '2018-07-02 8:00', 'C',13),
(6, '2018-07-01 6:00', '2018-07-01 8:00', 'D',11)
CREATE TABLE dbo.Event ([ID] INT, [Date] DateTime, [Type] varchar(1), [User] INT)
INSERT INTO [dbo].Event ([ID], [Date], [Type],[User])
VALUES
(0, '2018-07-01 12:00', 'A',10),
(0, '2018-07-05 15:00', 'A',11),
(0, '2018-07-03 13:00', 'C',10),
(0, '2018-07-10 9:00', 'B',12),
(0, '2018-07-01 5:00', 'C',10),
(0, '2018-07-01 10:00', 'D',11)
Expected result, Event now looks like this:
1 2018-07-01 12:00:00.000 A 10
2 2018-07-05 15:00:00.000 A 11
3 2018-07-03 13:00:00.000 C 10
0 2018-07-10 09:00:00.000 B 12
1 2018-07-01 05:00:00.000 C 10
6 2018-07-01 10:00:00.000 D 11
I don't know, if I fully got the logic, but this might help to get you running:
USE master;
GO
CREATE DATABASE TestDB
GO
USE TestDB;
GO
CREATE TABLE dbo.Episode ([ID] INT, [Start] DateTime, [End] DateTime, [Type] varchar(1), [User] INT)
INSERT INTO [dbo].Episode ([ID], [Start], [End], [Type],[User])
VALUES
(1, '2018-07-01 10:00', '2018-07-02 14:00', 'A',10),
(2, '2018-07-05 6:00', '2018-07-06 13:00', 'A',11),
(3, '2018-07-03 9:00', '2018-07-04 8:00', 'B',10),
(4, '2018-07-02 15:00', '2018-07-03 7:00', 'B',12),
(5, '2018-07-01 1:00', '2018-07-02 8:00', 'C',13),
(6, '2018-07-01 6:00', '2018-07-01 8:00', 'D',11)
CREATE TABLE dbo.[Event] ([ID] INT, [Date] DateTime, [Type] varchar(1), [User] INT)
INSERT INTO [dbo].[Event] ([ID], [Date], [Type],[User])
VALUES
(0, '2018-07-01 12:00', 'A',10),
(0, '2018-07-05 15:00', 'A',11),
(0, '2018-07-03 13:00', 'C',10),
(0, '2018-07-10 9:00', 'B',12),
(0, '2018-07-01 5:00', 'C',10),
(0, '2018-07-01 10:00', 'D',11)
GO
CREATE TABLE #EventList(Event Varchar(50), RN INT);
INSERT INTO #EventList VALUES ('A', 1),('B', 2),('C', 3),('D', 4),('E', 5),('F', 6);
WITH mathingEpisodes AS
(
SELECT ev.ID AS evID
,ev.[Date] AS evDate
,ev.[Type] AS evType
,ev.[User] AS evUser
,e1.RN AS evRN
,ep.ID AS epID
,ep.[Type] AS epType
,e2.RN AS epRN
FROM [Event] ev
LEFT JOIN Episode ep ON ev.[User]=ep.[User] AND ev.[Date] >= ep.[Start] AND ev.[Date] < ep.[End]
LEFT JOIN #EventList e1 ON ev.[Type]=e1.[Event]
LEFT JOIN #EventList e2 ON ep.[Type]=e2.[Event]
)
SELECT COALESCE(epID,Closest.ID) AS FittingEpisodeID
,me.evDate
,evType
,evUser
FROM mathingEpisodes me
OUTER APPLY(SELECT TOP 1 *
FROM Episode ep
CROSS APPLY(SELECT ABS(DATEDIFF(SECOND,me.evDate,ep.[Start])) AS DiffToStart
,ABS(DATEDIFF(SECOND,me.evDate,ep.[End])) AS DiffToEnd) Diffs
CROSS APPLY(SELECT CASE WHEN DiffToStart<DiffToEnd THEN DiffToStart ELSE DiffToEnd END AS Smaller) Diffs2
WHERE ep.[User] = me.evUser
AND me.epID IS NULL
ORDER BY Diffs2.Smaller
) Closest
ORDER BY evDate;
GO
USE master;
GO
DROP DATABASE TestDB;
GO
DROP TABLE #EventList
GO
The result
1 2018-01-07 05:00:00.000 C 10
6 2018-01-07 10:00:00.000 D 11
1 2018-01-07 12:00:00.000 A 10
3 2018-03-07 13:00:00.000 C 10
2 2018-05-07 15:00:00.000 A 11
4 2018-10-07 09:00:00.000 B 12
Some explanation
In the first cte I try to find fitting episodes (same user and date within range).
The second cte will compute the closest Episode for the same user in all cases, where the first cte did not succeed.
The only difference for this sample is the event for userId=12. My logic will bind this to the closest episode of this user (ID=4), while your expected output shows a zero in this place.
Anyway, my solution is fully set-based, therefore faster than a loop, and should be rather close to your needs. Try to adapt it...
UPDATE Some more thoughts...
I did not get the ghist of your #EventList... I bound the results into the set (you can make it visible by using SELECT * instead of the explicit column list. But this is - assumably - not what you meant...

Total Number of Leaves of same type in a month

I have 2 tables name EmployeeInfo and Leave and I am storing the values that which employee have taken which type of leave in month and how many times.
I am trying to calculate the number of leaves of same type but I'm stuck at one point for long time.
IF EXISTS(SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('Leave'))
BEGIN;
DROP TABLE [Leave];
END;
GO
IF EXISTS(SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('EmployeeInfo'))
BEGIN;
DROP TABLE [EmployeeInfo];
END;
GO
CREATE TABLE [EmployeeInfo] (
[EmpID] INT NOT NULL PRIMARY KEY,
[EmployeeName] VARCHAR(255)
);
CREATE TABLE [Leave] (
[LeaveID] INT NOT NULL PRIMARY KEY,
[LeaveType] VARCHAR(255) NULL,
[DateFrom] VARCHAR(255),
[DateTo] VARCHAR(255),
[Approved] Binary,
[EmpID] INT FOREIGN KEY REFERENCES EmployeeInfo(EmpID)
);
GO
INSERT INTO EmployeeInfo([EmpID], [EmployeeName]) VALUES
(1, 'Marcia'),
(2, 'Lacey'),
(3, 'Fay'),
(4, 'Mohammad'),
(5, 'Mike')
INSERT INTO Leave([LeaveID],[LeaveType],[DateFrom],[DateTo], [Approved], [EmpID]) VALUES
(1, 'Annual Leave','2018-01-08 04:52:03','2018-01-10 20:30:53', 1, 1),
(2, 'Sick Leave','2018-02-10 03:34:41','2018-02-14 04:52:14', 0, 2),
(3, 'Casual Leave','2018-01-04 11:06:18','2018-01-05 04:11:00', 1, 3),
(4, 'Annual Leave','2018-01-17 17:09:34','2018-01-21 14:30:44', 0, 4),
(5, 'Casual Leave','2018-01-09 23:31:16','2018-01-12 15:11:17', 1, 3),
(6, 'Annual Leave','2018-02-16 18:01:03','2018-02-19 17:16:04', 1, 2)
My query which I have tried so far look something like this.
SELECT Info.EmployeeName, Leave.LeaveType, SUM(DATEDIFF(Day, Leave.DateFrom, Leave.DateTo)) [#OfLeaves], DatePart(MONTH, Leave.DateFrom)
FROM EmployeeInfo Info, Leave
WHERE Info.EmpID = Leave.EmpID AND Approved = 1
GROUP BY Info.EmployeeName, Leave.LeaveType, [Leave].[DateFrom], [Leave].[DateTo]
And the record like given below
EmployeeName LeaveType #OfLeaves MonthNumber
-------------- ----------------- ----------- -----------
Fay Casual Leave 1 1
Fay Casual Leave 3 1
Lacey Annual Leave 3 2
Marcia Annual Leave 2 1
I want the record to look like this
EmployeeName LeaveType #OfLeaves MonthNumber
-------------- ----------------- ----------- -----------
Fay Casual Leave 4 1
Lacey Annual Leave 3 2
Marcia Annual Leave 2 1
If you don't want to modify existing query due to some constraint, this might work:
Select iq.EmployeeName, iq.LeaveType, SUM(iq.#OfLeaves) as #OfLeaves, iq.MonthNumber
From (
SELECT Info.EmployeeName, Leave.LeaveType, SUM(DATEDIFF(Day, Leave.DateFrom, Leave.DateTo)) [#OfLeaves], DatePart(MONTH, Leave.DateFrom) as MonthNumber
FROM EmployeeInfo Info, Leave
WHERE Info.EmpID = Leave.EmpID AND Approved = 1
GROUP BY Info.EmployeeName, Leave.LeaveType, [Leave].[DateFrom], [Leave].[DateTo]
)iq
group by iq.EmployeeName, iq.LeaveType, iq.MonthNumber
This just need small adjustment with your query in the GROUP BY clause. Instead of grouping them by [Leave].[DateFrom] and [Leave].[DateTo] which causes the row to be separated, you need to group it with the calculated column that uses datepart.
SELECT Info.EmployeeName,
Leave.LeaveType,
SUM(DATEDIFF(Day, Leave.DateFrom, Leave.DateTo)) [#OfLeaves],
DatePart(MONTH, Leave.DateFrom)
FROM EmployeeInfo Info
INNER JOIN Leave
ON Info.EmpID = Leave.EmpID
WHERE Approved = 1
GROUP BY Info.EmployeeName,
Leave.LeaveType,
DatePart(MONTH, Leave.DateFrom) -- <<<< change only this part
Here's a Demo.
I have also modified the syntax into ANSI format.

apply query to each part of table individually

I need to run some query against each rowset in a table (Azure SQL):
ID CustomerID MsgTimestamp Msg
-------------------------------------------------
1 123 2017-01-01 10:00:00 Hello
2 123 2017-01-01 10:01:00 Hello again
3 123 2017-01-01 10:02:00 Can you help me with my order
4 123 2017-01-01 11:00:00 Are you still there
5 456 2017-01-01 10:07:00 Hey I'm a new customer
What I want to do is to extract "chat session" for every customer from message records, that is, if the gap between someone's two consecutive messages is less than 30 minutes, they belong to the same session. I need to record the start and end time of each session in a new table. In the example above, start and end time of the first session for customer 123 are 10:00 and 10:02.
I know I can always use cursor and temp table to achieve that goal, but I'm thinking about utilizing any pre-built mechanism to reach better performance. Please kindly give me some input.
You can use window functions instead of cursor. Something like this should work:
declare #t table (ID int, CustomerID int, MsgTimestamp datetime2(0), Msg nvarchar(100))
insert #t values
(1, 123, '2017-01-01 10:00:00', 'Hello'),
(2, 123, '2017-01-01 10:01:00', 'Hello again'),
(3, 123, '2017-01-01 10:02:00', 'Can you help me with my order'),
(4, 123, '2017-01-01 11:00:00', 'Are you still there'),
(5, 456, '2017-01-01 10:07:00', 'Hey I''m a new customer')
;with x as (
select *, case when datediff(minute, lag(msgtimestamp, 1, '19000101') over(partition by customerid order by msgtimestamp), msgtimestamp) > 30 then 1 else 0 end as g
from #t
),
y as (
select *, sum(g) over(order by msgtimestamp) as gg
from x
)
select customerid, min(msgtimestamp), max(msgtimestamp)
from y
group by customerid, gg

Resources