Capturing Wait Statistics - sql-server

Based on https://www.simple-talk.com/books/sql-books/sql-server-performance-tuning-using-wait-statistics-a-beginners-guide/ (page 34), I'm trying to create table and fill it with Wait Stats.
IF NOT EXISTS (
SELECT *
FROM [sys].[tables]
WHERE [name]=N'WaitStats'AND[type] =N'U'
)
CREATE TABLE [dbo].[T_WaitStats](
[RowNum] [BIGINT] IDENTITY(1, 1) ,
[CaptureDate] [DATETIME] ,
[WaitType] [NVARCHAR](120) ,
[Wait_S] [DECIMAL](14, 2) ,
[Resource_S] [DECIMAL](14, 2),
[Signal_S] [DECIMAL](14, 2) ,
[WaitCount] [BIGINT] ,
[Percentage] [DECIMAL](4, 2) ,
[AvgWait_S] [DECIMAL](14, 2),
[AvgRes_S] [DECIMAL](14, 2),
[AvgSig_S] [DECIMAL](14, 2));
GO
INSERT INTO dbo.T_WaitStats([WaitType])
VALUES ('Wait Statistics for '+ CAST(GETDATE() AS NVARCHAR(19)));
INSERT INTO dbo.T_WaitStats(
[CaptureDate],
[WaitType],
[Wait_S],
[Resource_S],
[Signal_S],
[WaitCount],
[Percentage],
[AvgWait_S],
[AvgRes_S],
[AvgSig_S]
)
EXEC(
'WITH [Waits] AS(
SELECT
[wait_type],
[wait_time_ms] / 1000.0 AS [Wait_S],
([wait_time_ms] - [signal_wait_time_ms]) / 1000.0 AS [Resource_S],
[signal_wait_time_ms] / 1000.0 AS [Signal_S],
[waiting_tasks_count] AS [WaitCount],
100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() AS [Percentage],
ROW_NUMBER() OVER(ORDER BY [wait_time_ms] DESC) AS [RowNum]
FROM sys.dm_os_wait_stats
WHERE [wait_type] NOT IN (
N''BROKER_EVENTHANDLER'', N''BROKER_RECEIVE_WAITFOR'',
N''BROKER_TASK_STOP'', N''BROKER_TO_FLUSH'',
N''BROKER_TRANSMITTER'', N''CHECKPOINT_QUEUE'',
N''CHKPT'', N''CLR_AUTO_EVENT'',
N''CLR_MANUAL_EVENT''
)
)
SELECT
GETDATE() AS [CaptureDate],
[W1].[wait_type] AS [WaitType],
CAST ([W1].[Wait_S] AS DECIMAL(14, 2)) AS [Wait_S],
CAST ([W1].[Resource_S] AS DECIMAL(14, 2)) AS [Resource_S],
CAST ([W1].[Signal_S] AS DECIMAL(14, 2)) AS [Signal_S],
[W1].[WaitCount] AS [WaitCount],
CAST ([W1].[Percentage] AS DECIMAL(4, 2)) AS [Percentage],
CAST (([W1].[Wait_S] / [W1].[WaitCount]) AS DECIMAL (14, 4))
AS [AvgWait_S],
CAST (([W1].[Resource_S] / [W1].[WaitCount]) AS DECIMAL (14, 4))
AS [AvgWait_S],
CAST (([W1].[Resource_S] / [W1].[WaitCount]) AS DECIMAL (14, 4))
AS [AvgRes_S],
CAST (([W1].[Signal_S] / [W1].[WaitCount]) AS DECIMAL (14, 4))
AS [AvgSig_S]
FROM [Waits] AS [W1]
INNER JOIN [Waits] AS [W2]
ON [W2].[RowNum] <= [W1].[RowNum]
GROUP BY [W1].[RowNum], [W1].[wait_type], [W1].[Wait_S],
[W1].[Resource_S], [W1].[Signal_S], [W1].[WaitCount],
[W1].[Percentage]
HAVING SUM ([W2].[Percentage]) - [W1].[Percentage] < 95;'
);
First insert works, but second give me an error:
(1 row(s) affected) Msg 213, Level 16, State 7, Line 1 Column name or
number of supplied values does not match table definition.
Running second statement without insert (only EXEC part) works.
What might cause this error?

copy-paste in your query (returns 11 columns, but table expected 10 columns)
CAST (([W1].[Resource_S] / [W1].[WaitCount]) AS DECIMAL (14, 4)) AS [AvgWait_S],
query -
IF OBJECT_ID('dbo.T_WaitStats', 'U') IS NOT NULL
DROP TABLE dbo.T_WaitStats
GO
CREATE TABLE [dbo].[T_WaitStats] (
RowNum INT IDENTITY (1, 1) PRIMARY KEY,
CaptureDate DATETIME,
WaitType NVARCHAR(120),
Wait_S DECIMAL(14,2),
Resource_S DECIMAL(14,2),
Signal_S DECIMAL(14,2),
WaitCount BIGINT,
Percentage DECIMAL(4,2),
AvgWait_S DECIMAL(14,2),
AvgRes_S DECIMAL(14,2),
AvgSig_S DECIMAL(14,2)
)
GO
INSERT INTO dbo.T_WaitStats ([WaitType])
VALUES ('Wait Statistics for ' + CAST(GETDATE() AS NVARCHAR(19)))
INSERT INTO dbo.T_WaitStats (CaptureDate, WaitType, Wait_S, Resource_S, Signal_S, WaitCount, Percentage, AvgWait_S, AvgRes_S, AvgSig_S)
EXEC ('WITH [Waits] AS(
SELECT
[wait_type],
[wait_time_ms] / 1000.0 AS [Wait_S],
([wait_time_ms] - [signal_wait_time_ms]) / 1000.0 AS [Resource_S],
[signal_wait_time_ms] / 1000.0 AS [Signal_S],
[waiting_tasks_count] AS [WaitCount],
100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() AS [Percentage],
ROW_NUMBER() OVER(ORDER BY [wait_time_ms] DESC) AS [RowNum]
FROM sys.dm_os_wait_stats
WHERE [wait_type] NOT IN (
N''BROKER_EVENTHANDLER'', N''BROKER_RECEIVE_WAITFOR'',
N''BROKER_TASK_STOP'', N''BROKER_TO_FLUSH'',
N''BROKER_TRANSMITTER'', N''CHECKPOINT_QUEUE'',
N''CHKPT'', N''CLR_AUTO_EVENT'',
N''CLR_MANUAL_EVENT''
)
)
SELECT
GETDATE() AS [CaptureDate],
[W1].[wait_type] AS [WaitType],
[W1].[Wait_S] AS [Wait_S],
[W1].[Resource_S] AS [Resource_S],
[W1].[Signal_S] AS [Signal_S],
[W1].[WaitCount] AS [WaitCount],
[W1].[Percentage] AS [Percentage],
[W1].[Wait_S] / [W1].[WaitCount] AS [AvgWait_S],
[W1].[Resource_S] / [W1].[WaitCount] AS [AvgRes_S],
[W1].[Signal_S] / [W1].[WaitCount] AS [AvgSig_S]
FROM [Waits] AS [W1]
INNER JOIN [Waits] AS [W2]
ON [W2].[RowNum] <= [W1].[RowNum]
GROUP BY [W1].[RowNum], [W1].[wait_type], [W1].[Wait_S],
[W1].[Resource_S], [W1].[Signal_S], [W1].[WaitCount],
[W1].[Percentage]
HAVING SUM ([W2].[Percentage]) - [W1].[Percentage] < 95;'
);

Related

Calculate monthly hours

CREATE TABLE EmpAttendance
(
EmpCode INT,
Time_In Time(5),
Time_Out Time(5),
DayDate date
)
INSERT INTO EmpAttendance VALUES (23, '08:30', '13:00', '2018-07-12');
INSERT INTO EmpAttendance VALUES (23, '13:45', '18:30', '2018-07-12');
INSERT INTO EmpAttendance VALUES (23, '09:15', '12:50', '2018-07-13');
INSERT INTO EmpAttendance VALUES (23, '14:02', '18:22', '2018-07-13');
INSERT INTO EmpAttendance VALUES (23, '08:30', '16:00', '2018-07-14');
INSERT INTO EmpAttendance VALUES (23, '08:45', '17:56', '2018-07-15');
INSERT INTO EmpAttendance VALUES (12, '09:15', '12:50', '2018-07-13');
INSERT INTO EmpAttendance VALUES (12, '14:02', '18:22', '2018-07-13');
INSERT INTO EmpAttendance VALUES (12, '08:30', '16:00', '2018-07-14');
I have this table structure that is storing the time in and out of specific employee on a specific day. I want to calculate the total number of hours that an employee was present for a whole month.
The query should accept 2 input date parameters and employee code and give the output as total hours.
I am able to get daily hours but I want to calculate monthly as well and can't figure out how.
SELECT EmpCode, [DayDate],
FirstIN = CAST(MIN(Time_In) AS TIME),
LastOUT = CAST(MAX(Time_Out) AS TIME),
HoursSpent = DATEDIFF(HOUR, CAST(MIN(Time_In) AS TIME), CAST(MAX(Time_Out) AS TIME)),
CONVERT(VARCHAR(6), Datediff(second, CAST(MIN(Time_In) AS TIME), CAST(MAX(Time_Out) AS TIME))/3600)
+ ':'
+ RIGHT('0' + CONVERT(VARCHAR(2), (Datediff(second, CAST(MIN(Time_In) AS TIME), CAST(MAX(Time_Out) AS TIME)) % 3600) / 60), 2)
+ ':'
+ RIGHT('0' + CONVERT(VARCHAR(2), Datediff(second, CAST(MIN(Time_In) AS TIME), CAST(MAX(Time_Out) AS TIME)) % 60) , 2 )
AS hoursSpent
FROM EmpAttendance
GROUP BY EmpCode, DayDate
just change it to GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, DayDate), 0)
SELECT EmpCode,
DATEADD(MONTH, DATEDIFF(MONTH, 0, DayDate), 0) as MonthDate,
--FirstIN = CAST(MIN(Time_In) AS TIME),
--LastOUT = CAST(MAX(Time_Out) AS TIME),
HoursSpent = DATEDIFF(HOUR, CAST(MIN(Time_In) AS TIME), CAST(MAX(Time_Out) AS TIME)),
CONVERT(VARCHAR(6), Datediff(second, CAST(MIN(Time_In) AS TIME), CAST(MAX(Time_Out) AS TIME))/3600)
+ ':'
+ RIGHT('0' + CONVERT(VARCHAR(2), (Datediff(second, CAST(MIN(Time_In) AS TIME), CAST(MAX(Time_Out) AS TIME)) % 3600) / 60), 2)
+ ':'
+ RIGHT('0' + CONVERT(VARCHAR(2), Datediff(second, CAST(MIN(Time_In) AS TIME), CAST(MAX(Time_Out) AS TIME)) % 60) , 2 )
AS HoursSpent
FROM EmpAttendance
-- add the condition here in where clause
WHERE DayDate >= #StartDate
GROUP BY EmpCode, DATEADD(MONTH, DATEDIFF(MONTH, 0, DayDate), 0)
Assuming your Time_In and Time_Out values always relate to the same day, you can just sum up the datediff in minutes for each day:
declare #t table(EmpCode int
,Time_In time(5)
,Time_Out time(5)
,DayDate date
);
insert into #t values(23, '08:30', '13:00', '2018-07-12'),(23, '13:45', '18:30', '2018-07-12'),(23, '09:15', '12:50', '2018-07-13'),(23, '14:02', '18:22', '2018-07-13'),(23, '08:30', '16:00', '2018-07-14'),(23, '08:45', '17:56', '2018-07-15'),(12, '09:15', '12:50', '2018-07-13'),(12, '14:02', '18:22', '2018-07-13'),(12, '08:30', '16:00', '2018-07-14');
select EmpCode
,dateadd(month,datediff(month,0,DayDate),0) as MonthGroup
,sum(datediff(minute,Time_In,Time_Out))/60 as HoursWorked
,sum(datediff(minute,Time_In,Time_Out))%60 as MinutesWorked
from #t
group by EmpCode
,dateadd(month,datediff(month,0,DayDate),0)
;
Output:
+---------+-------------------------+-------------+---------------+
| EmpCode | MonthGroup | HoursWorked | MinutesWorked |
+---------+-------------------------+-------------+---------------+
| 12 | 2018-07-01 00:00:00.000 | 15 | 25 |
| 23 | 2018-07-01 00:00:00.000 | 33 | 51 |
+---------+-------------------------+-------------+---------------+

Calculate multiple shifts time difference in SQL Server

I have a question about SQL Server. Please tell me how to solve login and logout time calculation in SQL Server based on conditions.
if same empid will work multiple shifts, multiple shifts calculation for same date must be time difference is 5 hours then that date consider as multiple shifts for that emp
OnFloor time how much time he spend
OffFloor time how much time he spend
if logout is missed then consider as taligate is 1 or -1
Sample input data :
CREATE TABLE [dbo].[emplogindetails]
(
[Emp ID] [float] NULL,
[Area Of Access] [nvarchar](255) NULL,
[Time] [datetime] NULL
) ON [PRIMARY]
GO
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4F White Rm IN', CAST(N'2017-08-02T09:00:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4FWhite Rm OUT', CAST(N'2017-08-02T10:30:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4F White Rm IN', CAST(N'2017-08-03T09:30:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4FWhite Rm OUT', CAST(N'2017-08-03T12:30:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4F White Rm IN', CAST(N'2017-08-03T12:40:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4FWhite Rm OUT', CAST(N'2017-08-03T17:10:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4F White Rm IN', CAST(N'2017-08-03T06:30:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4FWhite Rm OUT', CAST(N'2017-08-03T08:30:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4F White Rm IN', CAST(N'2017-08-05T23:30:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4FWhite Rm OUT', CAST(N'2017-08-06T01:55:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4F White Rm IN', CAST(N'2017-08-06T02:15:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4FWhite Rm OUT', CAST(N'2017-08-06T06:10:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4F White Rm IN', CAST(N'2017-08-02T11:00:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4FWhite Rm OUT', CAST(N'2017-08-02T12:00:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4F White Rm IN', CAST(N'2017-08-02T13:00:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4F White Rm IN', CAST(N'2017-08-06T14:01:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4FWhite Rm OUT', CAST(N'2017-08-06T15:01:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4F White Rm IN', CAST(N'2017-08-06T15:20:00.000' AS DateTime))
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time])
VALUES (1, N'K4FWhite Rm OUT', CAST(N'2017-08-06T20:01:00.000' AS DateTime))
GO
Based on above data 4 th dates is holiday and he entered 5th date night shift and logout 6th date and same 6 th date he did another shift.
I want an output like below:
ShiftDate |ShitStartTime |ShiftEndTime |Total_Time |OnFloor |OffFloor |EmpID |Incount |OutCount |Tailgate
08/05/2017 |2017-08-05 23:30:00.000 |2017-08-06 06:10:00.000 |6:40:00 |06:00:00 |00:40:00 |1 |2 | 2 | 0
08/02/2017 |2017-08-02 09:00:00.000 |2017-08-02 13:00:00.000 |04:00:00 |02:30:00 |01:30:00 |1 |3 | 2 | 1
08/03/2017 |2017-08-03 06:30:00.000 |2017-08-03 17:10:00.000 |10:40:00 |09:30:00 |01:10:00 |1 |3 | 3 | 0
08/06/2017 |2017-08-06 14:01:00.000 |2017-08-06 20:01:00.000 |6:00:00 |05:10:00 |00:50:00 |1 |2 | 2 | 0
I tried like this:
select
isnull( ShiftDate ,'1900-01-01')as ShiftDate ,
isnull( min(logintime) ,'1900-01-01') as ShitStartTime,
max( case when logouttime is null then '1900-01-01' else logouttime end )ShiftEndTime ,
convert(varchar(8),dateadd(ss,sum(datediff(second,0,dateadd(day,0,Total_Time))),0),108) Total_Time,
convert(varchar(8),dateadd(ss,sum(datediff(second,0,dateadd(day,0,OnFloor))),0),108) OnFloor,
convert(varchar(8),dateadd(ss,sum(datediff(second,0,dateadd(day,0,OffFloor))),0),108) OffFloor,
EmpID ,Incount ,OutCount, Tailgate
from (
select
CONVERT(VARCHAR(12), ( OffFloor + cast ( OnFloor as int)) / 60 / 60 % 24)
+':'+ CONVERT(VARCHAR(2), (OffFloor + cast ( OnFloor as int)) / 60 % 60)
+':'+ CONVERT(VARCHAR(2), (OffFloor + cast ( OnFloor as int)) % 60) as Total_Time
,case when convert( varchar(10),OnFloor )='0' then '0:0:0' else OnFloor end OnFloor
, CONVERT(VARCHAR(12), (OffFloor) / 60 / 60 % 24) +':'+ CONVERT(VARCHAR(2), (OffFloor) / 60 % 60)
+':'+ CONVERT(VARCHAR(2), (OffFloor) % 60) AS OffFloor
,[Emp ID] ,[Area Of Access],status,logintime,logouttime
from (
select isnull( DATEDIFF(SECOND, a.logintime, a.logouttime) ,0) OffFloor , cast ( '0' as varchar) as OnFloor
,[Emp ID] ,[Area Of Access],status,logintime,logouttime
from (
SELECT o.time logouttime,i.Time logintime,i.[Emp ID]
,substring ( i.[Area Of Access] ,charindex('out',i.[Area Of Access]),len(i.[Area Of Access])) status
,i.[Area Of Access]
FROM test.dbo.emplogindetails i left join test.dbo.emplogindetails o
on i.[emp id] = o.[emp id]
AND CONVERT(date, i.time) = CONVERT(date, o.time)
AND o.time > i.time
AND substring ( o.[Area Of Access] ,charindex('in',o.[Area Of Access]),len(o.[Area Of Access]))='in'
and substring ( i.[Area Of Access] ,charindex('out',i.[Area Of Access]),len(i.[Area Of Access]))='out'
and o.Time=(SELECT MIN(o2.time)
FROM test.dbo.emplogindetails o2
WHERE o2.time > i.time
and o2.[Emp ID]=i.[Emp ID]
---and [emp id]='105828'
)
--where i.[emp id]='105828'
)a where a.status='out')a
union all
select CONVERT(VARCHAR(12), (OffFloor + OnFloor) / 60 / 60 % 24)
+':'+ CONVERT(VARCHAR(2), (OffFloor + OnFloor) / 60 % 60)
+':'+ CONVERT(VARCHAR(2), (OffFloor + OnFloor) % 60) as Calculated_Time
, CONVERT(VARCHAR(12), (OnFloor) / 60 / 60 % 24)
+':'+ CONVERT(VARCHAR(2), (OnFloor) / 60 % 60)+':'+ CONVERT(VARCHAR(2), (OnFloor) % 60) AS OnFloor
, case when convert( varchar(10),OffFloor) ='0' then '0:0:0' else OffFloor end OffFloor
,[Emp ID] ,[Area Of Access],
status,logintime,logouttime
from (
select '0' as OffFloor, isnull( DATEDIFF(SECOND, a.logintime, a.logouttime) ,0) OnFloor
,[Emp ID] ,[Area Of Access],status
,logintime,logouttime
from (
SELECT o.time logouttime,i.Time logintime,i.[Emp ID]
,substring ( i.[Area Of Access] ,charindex('in',i.[Area Of Access]),len(i.[Area Of Access])) status
,i.[Area Of Access]
FROM test.dbo.emplogindetails i left join test.dbo.emplogindetails o
on i.[emp id] = o.[emp id]
AND CONVERT(date, i.time) = CONVERT(date, o.time)
AND o.time > i.time
AND substring ( o.[Area Of Access] ,charindex('out',o.[Area Of Access]),len(o.[Area Of Access]))='out'
and substring ( i.[Area Of Access] ,charindex('in',i.[Area Of Access]),len(i.[Area Of Access]))='in'
and o.Time=(SELECT MIN(o2.time)
FROM test.dbo.emplogindetails o2
WHERE o2.time > i.time
and o2.[Emp ID]=i.[Emp ID]
---and [emp id]='105828'
)
---where i.[emp id]='105828'
)a where a.status='in')stag)stag
join
----get incount and outcount and tailgate information
(select [emp id]as empid,incount,outcount,
isnull( incount-outcount ,0) as Tailgate ,Date as ShiftDate
from (
select
i.[Emp ID] ,convert(varchar(10),time,101) as Date,
count( case when substring ( i.[Area Of Access] ,charindex('in',i.[Area Of Access]),len(i.[Area Of Access]))='in'
then 'in' end )Incount
,count( case when substring ( i.[Area Of Access] ,charindex('out',i.[Area Of Access]),len(i.[Area Of Access]))='out'
then 'out' end )outcount
FROM test.dbo.emplogindetails i
--where [emp id]='105828'
group by i.[Emp ID],convert(varchar(10),time,101)
)cnt)cnt
on stag.[Emp ID]=cnt.empid and convert(varchar(10),stag.logintime,101)=cnt.ShiftDate
group by EmpID ,Incount ,OutCount, Tailgate
,isnull( ShiftDate ,'1900-01-01')
This query is not returning the expected result when same date have multiple shifts calculation if logout and login time difference more than 5 hours then consider as next shift.
as per given below logic not working for empid=5 and data looking like below.
Hi ,one records is failed as per our logic .some changes is required in logic. I did not get expected ouput for below example.
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time]) VALUES (5, N'K4F White Rm IN', CAST(N'2017-08-02T23:30:00.000' AS DateTime))
GO
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time]) VALUES (5, N' K4FWhite Rm OUT', CAST(N'2017-08-03T01:30:00.000' AS DateTime))
GO
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time]) VALUES (5, N'K4F White Rm IN', CAST(N'2017-08-03T01:40:00.000' AS DateTime))
GO
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time]) VALUES (5, N'K4F White Rm OUT', CAST(N'2017-08-03T04:00:00.000' AS DateTime))
GO
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time]) VALUES (5, N'K4F White Rm IN', CAST(N'2017-08-03T04:30:00.000' AS DateTime))
GO
INSERT [dbo].[emplogindetails] ([Emp ID], [Area Of Access], [Time]) VALUES (5, N'K4F White Rm OUT', CAST(N'2017-08-03T06:00:00.000' AS DateTime))
GO
Please tell me how to achieve this task in SQL Server
This query returns your expected output
select
cast(ShitStartTime as date) ShiftDate, ShitStartTime, ShiftEndTime
, concat(right(concat('0', tTime/60), 2), ':', right(concat('0',tTime%60), 2)) Total_Time
, concat(right(concat('0', onF/60), 2), ':', right(concat('0',onF%60), 2)) OnFloor
, concat(right(concat('0', offF/60), 2), ':', right(concat('0',offF%60), 2)) OffFloor
, [Emp Id], Incount, OutCount, Tailgate
from (
select
[Emp Id], min(Time) ShitStartTime, max(Time) ShiftEndTime, sum(iif([Area Of Access] = 'K4F White Rm IN', diff, 0)) onF
, sum(iif([Area Of Access] = 'K4FWhite Rm OUT', diff, 0)) offF, sum(diff) tTime
, sum(iif([Area Of Access] = 'K4F White Rm IN', 1, 0)) Incount
, sum(iif([Area Of Access] = 'K4FWhite Rm OUT', 1, 0)) OutCount
, max(Tailgate) Tailgate
from (
select
*, datediff(mi, Time, lead(Time) over (partition by [Emp Id], group_ order by Time)) diff
, iif(Time = max(Time) over (partition by [Emp Id], group_) and [Area Of Access] = 'K4F White Rm IN', 1, 0) Tailgate
from (
select
*, sum(gr) over (partition by [Emp Id] order by Time rows unbounded preceding) group_
from (
select
*, iif(datediff(hh, lag(Time) over (partition by [Emp Id] order by Time), Time) <= 5, 0, 1) gr
from
emplogindetails
) t
) t
) t
group by [Emp Id], group_
) t

Running Average for stock using Recursive CTE

I have the below data and need to calculate the running average for each row using the amount from each of the previous rows.
CREATE TABLE [dbo].[AKTest](
[IntakeSellingPrice] [decimal](38, 20) NULL,
[IntakeSellingAmount] [decimal](38, 6) NULL,
[Item No_] [nvarchar](20) NOT NULL,
[Variant Code] [nvarchar](10) NOT NULL,
[Unit of Measure Code] [nvarchar](10) NOT NULL,
[Posting Date] [datetime] NOT NULL,
[PurchaseQty] [decimal](38, 20) NULL,
[ReceiptNo] [bigint] NULL,
[InventoryBalance] [decimal](38, 20) NOT NULL,
[NewBalance] [decimal](38, 20) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[AKTest] ([IntakeSellingPrice], [IntakeSellingAmount], [Item No_], [Variant Code], [Unit of Measure Code], [Posting Date], [PurchaseQty], [ReceiptNo], [InventoryBalance], [NewBalance]) VALUES (CAST(10.00000000000000000000 AS Decimal(38, 20)), CAST(1000.000000 AS Decimal(38, 6)), N'1000001', N'NO_SIZE', N'EACH', CAST(0x0000A80800000000 AS DateTime), CAST(100.00000000000000000000 AS Decimal(38, 20)), 1, CAST(0.00000000000000000000 AS Decimal(38, 20)), CAST(100.00000000000000000000 AS Decimal(38, 20)))
GO
INSERT [dbo].[AKTest] ([IntakeSellingPrice], [IntakeSellingAmount], [Item No_], [Variant Code], [Unit of Measure Code], [Posting Date], [PurchaseQty], [ReceiptNo], [InventoryBalance], [NewBalance]) VALUES (CAST(5.00000000000000000000 AS Decimal(38, 20)), CAST(250.000000 AS Decimal(38, 6)), N'1000001', N'NO_SIZE', N'EACH', CAST(0x0000A80E00000000 AS DateTime), CAST(50.00000000000000000000 AS Decimal(38, 20)), 2, CAST(50.00000000000000000000 AS Decimal(38, 20)), CAST(100.00000000000000000000 AS Decimal(38, 20)))
GO
INSERT [dbo].[AKTest] ([IntakeSellingPrice], [IntakeSellingAmount], [Item No_], [Variant Code], [Unit of Measure Code], [Posting Date], [PurchaseQty], [ReceiptNo], [InventoryBalance], [NewBalance]) VALUES (CAST(12.50000000000000000000 AS Decimal(38, 20)), CAST(625.000000 AS Decimal(38, 6)), N'1000001', N'NO_SIZE', N'EACH', CAST(0x0000A81900000000 AS DateTime), CAST(50.00000000000000000000 AS Decimal(38, 20)), 3, CAST(60.00000000000000000000 AS Decimal(38, 20)), CAST(110.00000000000000000000 AS Decimal(38, 20)))
GO
Expected Outcome
ReceiptNo Average
_________________________
1 10.00
2 7.50
3 8.86
The Formular I used to calculate it manually is defined below for the third row. The calculation is better if you start at the bottom first.
A) I start at the bottom using receiptNo 3 where the NewBalance is 110.
B) 50 units are purchased for 12.50 = 625
C) that leaves 60 units. on the previous row 50 units are purchased for 5 = 250
D) that leaves 10 units. on the previous row 100 units are purchased for 10 = 1000. But we only need the cost of 10 so 1000/10 = 100.
E) add all the cost up 625 + 250 + 100 = 975 / 110 = 8.86
I don't think this is possible using ROWS BETWEEN and OVER as the logic is a bit odd?
I created a temporary table to play with the data and test the results, but basically this is just your original script with a recursive CTE added:
CREATE TABLE #AKTest (
[IntakeSellingPrice] [decimal](38, 20) NULL,
[IntakeSellingAmount] [decimal](38, 6) NULL,
[Item No_] [nvarchar](20) NOT NULL,
[Variant Code] [nvarchar](10) NOT NULL,
[Unit of Measure Code] [nvarchar](10) NOT NULL,
[Posting Date] [datetime] NOT NULL,
[PurchaseQty] [decimal](38, 20) NULL,
[ReceiptNo] [bigint] NULL,
[InventoryBalance] [decimal](38, 20) NOT NULL,
[NewBalance] [decimal](38, 20) NULL);
GO
INSERT #AKTest ([IntakeSellingPrice], [IntakeSellingAmount], [Item No_], [Variant Code], [Unit of Measure Code], [Posting Date], [PurchaseQty], [ReceiptNo], [InventoryBalance], [NewBalance]) VALUES (CAST(10.00000000000000000000 AS Decimal(38, 20)), CAST(1000.000000 AS Decimal(38, 6)), N'1000001', N'NO_SIZE', N'EACH', CAST(0x0000A80800000000 AS DateTime), CAST(100.00000000000000000000 AS Decimal(38, 20)), 1, CAST(0.00000000000000000000 AS Decimal(38, 20)), CAST(100.00000000000000000000 AS Decimal(38, 20)))
GO
INSERT #AKTest ([IntakeSellingPrice], [IntakeSellingAmount], [Item No_], [Variant Code], [Unit of Measure Code], [Posting Date], [PurchaseQty], [ReceiptNo], [InventoryBalance], [NewBalance]) VALUES (CAST(5.00000000000000000000 AS Decimal(38, 20)), CAST(250.000000 AS Decimal(38, 6)), N'1000001', N'NO_SIZE', N'EACH', CAST(0x0000A80E00000000 AS DateTime), CAST(50.00000000000000000000 AS Decimal(38, 20)), 2, CAST(50.00000000000000000000 AS Decimal(38, 20)), CAST(100.00000000000000000000 AS Decimal(38, 20)))
GO
INSERT #AKTest ([IntakeSellingPrice], [IntakeSellingAmount], [Item No_], [Variant Code], [Unit of Measure Code], [Posting Date], [PurchaseQty], [ReceiptNo], [InventoryBalance], [NewBalance]) VALUES (CAST(12.50000000000000000000 AS Decimal(38, 20)), CAST(625.000000 AS Decimal(38, 6)), N'1000001', N'NO_SIZE', N'EACH', CAST(0x0000A81900000000 AS DateTime), CAST(50.00000000000000000000 AS Decimal(38, 20)), 3, CAST(60.00000000000000000000 AS Decimal(38, 20)), CAST(110.00000000000000000000 AS Decimal(38, 20)))
GO
SELECT * FROM #AKTest;
WITH cte AS (
SELECT
ReceiptNo,
ReceiptNo AS linked_to,
NewBalance,
NewBalance - PurchaseQty AS remaining,
PurchaseQty AS purchased,
IntakeSellingPrice
FROM
#AKTest
UNION ALL
SELECT
c.ReceiptNo,
c.linked_to - 1 AS linked_to,
a.NewBalance,
c.remaining - a.PurchaseQty AS remaining,
CASE WHEN a.PurchaseQty > c.remaining THEN c.remaining ELSE a.PurchaseQty END AS purchased,
a.IntakeSellingPrice
FROM
cte c
INNER JOIN #AKTest a ON a.ReceiptNo = c.linked_to - 1
WHERE
c.linked_to > 1)
SELECT
ReceiptNo,
SUM(purchased * IntakeSellingPrice) / MAX(NewBalance) AS avg_price
FROM
cte
GROUP BY
ReceiptNo
ORDER BY
ReceiptNo;
Gets the correct answer:
ReceiptNo avg_price
1 10.000000
2 7.500000
3 8.863636
As requested, this will display all of the data from the table, with the average price on the end:
WITH cte AS (
SELECT
ReceiptNo,
ReceiptNo AS linked_to,
NewBalance,
NewBalance - PurchaseQty AS remaining,
PurchaseQty AS purchased,
IntakeSellingPrice
FROM
#AKTest
UNION ALL
SELECT
c.ReceiptNo,
c.linked_to - 1 AS linked_to,
a.NewBalance,
c.remaining - a.PurchaseQty AS remaining,
CASE WHEN a.PurchaseQty > c.remaining THEN c.remaining ELSE a.PurchaseQty END AS purchased,
a.IntakeSellingPrice
FROM
cte c
INNER JOIN #AKTest a ON a.ReceiptNo = c.linked_to - 1
WHERE
c.linked_to > 1),
Averages AS (
SELECT
ReceiptNo,
SUM(purchased * IntakeSellingPrice) / MAX(NewBalance) AS avg_price
FROM
cte
GROUP BY
ReceiptNo)
SELECT
a.*,
v.avg_price
FROM
Averages v
INNER JOIN #AKTest a ON a.ReceiptNo = v.ReceiptNo
ORDER BY
a.ReceiptNo;

SQL Server Windowing - 24 Hour Window

I have the following data
CREATE TABLE [dbo].[Test](
[CustId] [int] NULL,
[Spend] [money] NULL,
[TimeOdSpent] [datetime] NULL,
[ID] [int] IDENTITY(1,1) NOT NULL
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Test] ON
GO
INSERT [dbo].[Test] ([CustId], [Spend], [TimeOdSpent], [ID])
VALUES (11, 400.0000, CAST(N'2016-10-27 10:00:00.000' AS DateTime), 1)
INSERT [dbo].[Test] ([CustId], [Spend], [TimeOdSpent], [ID])
VALUES (11, 200.0000, CAST(N'2016-10-27 11:00:00.000' AS DateTime), 2)
INSERT [dbo].[Test] ([CustId], [Spend], [TimeOdSpent], [ID])
VALUES (11, 400.0000, CAST(N'2016-10-28 09:00:00.000' AS DateTime), 3)
INSERT [dbo].[Test] ([CustId], [Spend], [TimeOdSpent], [ID])
VALUES (11, 500.0000, CAST(N'2016-10-28 16:00:00.000' AS DateTime), 4)
GO
SET IDENTITY_INSERT [dbo].[Test] OFF
Expected Result should be like this
1 2016-10-27 11:00:00.000 600
2 2016-10-28 09:00:00.000 1000
3 2016-10-28 16:00:00.000 900
I want to find out the instances where the spend Totals > 500 within a 24 hour period. Being trying to write a windowing query without luck
You can query as below:
Select * from (
Select *, Sm = sum(spend) over(partition by convert(date,timeofuse)) from #customer
) a
Where Sm > 500
This is the sort of thing I was looking for. I used the Sales.SalesOrderHeader table from AdventureWorks Instead of my simple table above
;WITH cte1 as
(
select
LAG(ShipDate) OVER(PARTITION By SAlesPersonId ORDER BY ShipDate) ShipDateBefore,ShipDate, SalesPersonID,SubTotal,CAST(ShipDate as Date) Date
from Sales.SalesOrderHeader
where CAST(ShipDate as DATE)<'20080710' and SalesPersonID IS NOT NULL
),cte2 as
(Select * ,DATEDIFF(ss,ShipDateBefore,ShipDate) as DiffinDays
from cte1
), cte3 as (
select * ,SUM(DiffinDays) OVER(Partition BY SalesPersonId ORDER BY ShipDate ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as RunningTime
from cte2
),cte4 as
(select
*,ISNULL(CAST((RunningTime / 86400.00) AS INT),0) Cycle
FROM cte3
)
SELECT
SalesPersonID ,SUM(SubTotal)Total,MIN(ShipDate)DurationStart,MAX(ShipDate)DurationStart
from cte4
GROUP by SalesPersonID,Cycle
Having SUM(SubTotal) > 100000.00

How to calculate peak-valley drawdown with SQL Server 2012?

I'm wondering if some of the new SQL Server 2012 functions would help with this problem. Here's my DDL and sample data
CREATE TABLE [dbo].[transactions]
(
[transactionId] [int] NOT NULL,
[dt] [datetime] NOT NULL,
[balance] [decimal](22, 6) NULL
);
GO
INSERT [dbo].[transactions] ([transactionId], [dt], [balance]) VALUES
(174, CAST(0x0000A19600000000 AS DateTime), CAST(1000.000000 AS Decimal(22, 6))),
(178, CAST(0x0000A19700869520 AS DateTime), CAST(1100.000000 AS Decimal(22, 6))),
(179, CAST(0x0000A19700933780 AS DateTime), CAST(1212.000000 AS Decimal(22, 6))),
(180, CAST(0x0000A19700B4B9A0 AS DateTime), CAST(1342.000000 AS Decimal(22, 6))),
(181, CAST(0x0000A19700BB0AD0 AS DateTime), CAST(1198.000000 AS Decimal(22, 6))),
(182, CAST(0x0000A19700E67030 AS DateTime), CAST(1234.000000 AS Decimal(22, 6))),
(183, CAST(0x0000A19700F358E0 AS DateTime), CAST(900.000000 AS Decimal(22, 6))),
(184, CAST(0x0000A19700F58B60 AS DateTime), CAST(876.000000 AS Decimal(22, 6))),
(185, CAST(0x0000A19700F9AA10 AS DateTime), CAST(889.000000 AS Decimal(22, 6))),
(186, CAST(0x0000A19701034700 AS DateTime), CAST(1133.000000 AS Decimal(22, 6))),
(187, CAST(0x0000A19A0089E0E0 AS DateTime), CAST(1400.000000 AS Decimal(22, 6))),
(191, CAST(0x0000A19A009450C0 AS DateTime), CAST(1566.000000 AS Decimal(22, 6))),
(192, CAST(0x0000A19A00A5E4C0 AS DateTime), CAST(1800.000000 AS Decimal(22, 6))),
(188, CAST(0x0000A19A00AA49C0 AS DateTime), CAST(1900.000000 AS Decimal(22, 6))),
(189, CAST(0x0000A19A00B54640 AS DateTime), CAST(1456.000000 AS Decimal(22, 6))),
(190, CAST(0x0000A19A00CAB2A0 AS DateTime), CAST(1234.000000 AS Decimal(22, 6))),
(193, CAST(0x0000A19A00F12660 AS DateTime), CAST(1400.000000 AS Decimal(22, 6))),
(195, CAST(0x0000A19A010087E0 AS DateTime), CAST(1444.000000 AS Decimal(22, 6))),
(196, CAST(0x0000A19E00C7F380 AS DateTime), CAST(1556.000000 AS Decimal(22, 6))),
(197, CAST(0x0000A19E00FE5560 AS DateTime), CAST(1975.000000 AS Decimal(22, 6)));
I am after the largest percentage peak-valley drawdown of the balance for the series, ordered by dt. The peak to valley drawdown is the greatest percent change in a high in the balance to the lowest low before the previous high is crossed. Better described here http://www.investopedia.com/terms/p/peak-to-valley-drawdown.asp In this data set we have two drawdowns.
First one is from 1342.00 to 876.00 (-34.72%) and the second one from 1900 to 1234 (-35.05%)
The biggest peak to valley percent drawdown in this set therefore, is -35.05%. I need a SQL Server query that can provide this value. Would rather not have to use temp tables if possible. Any ideas?
I don't know that any SQL Server 2012 functionality will get this value any more succinctly or efficiently than this:
;WITH x AS
(
SELECT [drop] = ((s.balance-e.balance)*100.0/s.balance)
FROM dbo.transactions AS s
INNER JOIN dbo.transactions AS e
ON s.transactionId < e.transactionId
AND s.balance > e.balance
)
SELECT [Largest Drawdown] = -MAX([drop]) FROM x;
Result:
Largest Drawdown
----------------
-35.05263157894
I do confess, though, that this works for your sample data only because your valleys are convenient for the problem you want to solve. If you change the 4th-last row to 875 this query considers that a part of the set. In other words, I've calculated drawdown here for the entire range, rather than just the range until the high is crossed again.
I suspect there is a better way to solve this query using gap/island techniques and I will try to return to it when I can focus on it adequately.
This will miss if the first entry is a peak
;with trnsCTE (ID,bal) AS
( -- get seqential ID
SELECT ROW_NUMBER() OVER (ORDER BY DT) as ID, [balance]
from [transactions]
),
trnsCTE2 (ID,bal) AS
( -- any peaks
select t2.ID, t2.bal
from trnsCTE as T1
join trnsCTE as T2
on ( t2.ID = t1.ID+1
and t2.bal > t1.bal )
join trnsCTE as T3
on t3.ID = t2.ID+1
and t3.bal < t1.bal
)
,
trnsCTE3 (ID,bal) AS
( -- get first peak and then bigger peaks only
SELECT distinct T1.ID, T1.BAL
from trnsCTE2 as T1
where T1.ID = (select min(ID) from trnsCTE2)
or T1.bal > (select max(bal) from trnsCTE2 where trnsCTE2.ID < t1.ID)
)
-- calculate
select t1.id, t1.bal, min(trnsCTE.bal), (t1.bal - min(trnsCTE.bal)) * 100 / t1.bal
from trnsCTE
join trnsCTE3 t1
on t1.id < trnsCTE.id
and ( trnsCTE.id < (select min(id) from trnsCTE3 where id > t1.id)
or
t1.id = ( select max(id) from trnsCTE3 ) )
group by t1.id, t1.bal
order by t1.id
This translates directly to a #temps
Did not use #temp at OP said did not want to user #temp
insert into #trnsCTE (ID,bal)
SELECT ROW_NUMBER() OVER (ORDER BY DT) as ID, [balance]
from [transactions]
select peak_dt, peak_balance, trough_dt, trough_balance, (peak_balance - trough_balance) * 100.0 / peak_balance as drawdown
from (
select dt as peak_dt, balance as peak_balance, nullif(last_value(dt) over (partition by peak_valley_group order by dt rows between unbounded preceding and unbounded following), dt) as trough_dt, nullif(last_value(balance) over (partition by peak_valley_group order by dt rows between unbounded preceding and unbounded following), balance) as trough_balance, isPeak
from (
select *, sum(isPeak) over (order by dt) as peak_valley_group
from (
select dt, balance, (case when forward_trend = -1 then 1 else 0 end) as isPeak, max(balance) over (partition by forward_trend order by dt) as current_max_balance
from (
-- Nulls for lead/lag here produce the desired result
select *, (case when lead(balance, 1) over (order by dt) > balance then 1 else -1 end) as forward_trend, (case when lag(balance, 1) over (order by dt) > balance then 1 else -1 end) as backward_trend
from transactions
) t
where forward_trend = backward_trend
) t
where (isPeak = 1 and balance = current_max_balance)
or isPeak = 0
) t
) t
where isPeak = 1
order by peak_dt

Resources