我有一个出勤SQL表,用于存储员工的开始和结束日。每次打卡(打卡和打卡)都在一个单独的记录中。
我想计算每个员工在一个请求月份的总工作时间。
我尝试制作一个标量函数,它接受两个日期和员工ID并返回上述任务的计算,但它只计算所有日期之间一个日期的差异。
数据是这样的:
000781 2015-08-14 08:37:00 AM EMPIN 539309898
000781 2015-08-14 08:09:48 PM EMPOUT 539309886
我的代码是:
@FromDate NVARCHAR(10)
,@ToDate NVARCHAR(10)
,@EmpID NVARCHAR(6)
CONVERT(NVARCHAR,DATEDIFF(HOUR
,(SELECT Time from PERS_Attendance att where attt.date between convert(date,@fromDate) AND CONVERT(Date,@toDate)
AND (EmpID= @EmpID OR ISNULL(@EmpID, '') = '') AND Funckey = 'EMPIN')
,(SELECT Time from PERS_Attendance att where attt.date between convert(date,@fromDate) AND CONVERT(Date,@toDate)
AND (EmpID= @EmpID OR ISNULL(@EmpID, '') = '') AND Funckey = 'EMPOUT') ))
FROM PERS_Attendance attt
答案 0 :(得分:2)
我认为另一种方法简单而有效。
LEAD
以下是时间线的说明。请注意,start
时间发生在一个人“进入”时,end
时间也发生在一个人还在“进入”时:
我们需要做的就是计算每个事件(in
和out
)和start
时间之间的时间差的简单总和,然后为{{1}执行相同的操作} 时间。如果事件为end
,则添加的持续时间应为正号,如果事件为in
,则添加的持续时间应为负号。最终结果是结束时间的总和与开始时间的总和之间的差异。
out
我建议以分钟计算持续时间,然后将结果除以60得到小时数,但这实际上取决于您的要求。顺便说一句,将日期存储为summing for start:
|---| +
|----------| -
|-----------------| +
|--------------------------| -
|-------------------------------| +
--|====|--------|======|------|===|=====|---|==|---|===|====|----|=====|--- time
in out in out in start out in out in end out in out
summing for end:
|---| +
|-------| -
|----------| +
|--------------| -
|------------------------| +
|-------------------------------| -
|--------------------------------------| +
|-----------------------------------------------| -
|----------------------------------------------------| +
是个坏主意。
NVARCHAR
结束时间和开始时间之间有DECLARE @StartDate datetime = '2015-08-01 00:00:00';
DECLARE @EndDate datetime = '2015-09-01 00:00:00';
DECLARE @EmpID nvarchar(6) = NULL;
WITH
CTE_Start
AS
(
SELECT
EmpID
,SUM(DATEDIFF(minute, (CAST(att.[date] AS datetime) + att.[Time]), @StartDate)
* CASE WHEN Funckey = 'EMPIN' THEN +1 ELSE -1 END) AS SumStart
FROM
PERS_Attendance AS att
WHERE
(EmpID = @EmpID OR @EmpID IS NULL)
AND att.[date] < @StartDate
GROUP BY EmpID
)
,CTE_End
AS
(
SELECT
EmpID
,SUM(DATEDIFF(minute, (CAST(att.[date] AS datetime) + att.[Time]), @StartDate)
* CASE WHEN Funckey = 'EMPIN' THEN +1 ELSE -1 END) AS SumEnd
FROM
PERS_Attendance AS att
WHERE
(EmpID = @EmpID OR @EmpID IS NULL)
AND att.[date] < @EndDate
GROUP BY EmpID
)
SELECT
CTE_End.EmpID
,(SumEnd - ISNULL(SumStart, 0)) / 60.0 AS SumHours
FROM
CTE_End
LEFT JOIN CTE_Start ON CTE_Start.EmpID = CTE_End.EmpID
OPTION(RECOMPILE);
,因为在开始时间之前可能有LEFT JOIN
没有记录。
EmpID
非常有用。如果OPTION(RECOMPILE)
为@EmpID
,您将获得所有人的结果,如果不是NULL
,您只会获得一个人的结果。
如果所有人只需要一个号码(总计),请将计算结果包裹在NULL
SELECT
中。如果您总是想要所有人的总计,那么请完全删除SUM()
参数。
在@EmpID
上建立索引是个好主意。
答案 1 :(得分:1)
我的方法如下:
CREATE FUNCTION [dbo].[MonthlyHoursByEmpID]
(
@StartDate Date,
@EndDate Date,
@Employee NVARCHAR(6)
)
RETURNS FLOAT
AS
BEGIN
DECLARE @TotalHours FLOAT
DECLARE @In TABLE ([Date] Date, [Time] Time)
DECLARE @Out TABLE ([Date] Date, [Time] Time)
INSERT INTO @In([Date], [Time])
SELECT [Date], [Time]
FROM PERS_Attendance
WHERE [EmpID] = @Employee AND [Funckey] = 'EMPIN' AND ([Date] > @StartDate AND [Date] < @EndDate)
INSERT INTO @Out([Date], [Time])
SELECT [Date], [Time]
FROM PERS_Attendance
WHERE [EmpID] = @Employee AND [Funckey] = 'EMPOUT' AND ([Date] > @StartDate AND [Date] < @EndDate)
SET @TotalHours = (SELECT SUM(CONVERT([float],datediff(minute,I.[Time], O.[Time]))/(60))
FROM @in I
INNER JOIN @Out O
ON I.[Date] = O.[Date])
RETURN @TotalHours
END
答案 2 :(得分:0)
假设条目已正确配对(in -> out -> in -> out -> in
等)。
SQL Server 2012及更高版本:
DECLARE @Year int = 2015
DECLARE @Month int = 8
;WITH
cte AS (
SELECT EmpID,
InDate = LAG([Date], 1) OVER (PARTITION BY EmpID ORDER BY [Date]),
OutDate = [Date],
HoursWorked = DATEDIFF(hour, LAG([Date], 1) OVER (PARTITION BY EmpID ORDER BY [Date]), [Date]),
Funckey
FROM PERS_Attendance
)
SELECT EmpID,
TotalHours = SUM(HoursWorked)
FROM cte
WHERE Funckey = 'EMPOUT'
AND YEAR(InDate) = @Year
AND MONTH(InDate) = @Month
GROUP BY EmpID
SQL Server 2005及更高版本:
;WITH
cte1 AS (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY EmpID ORDER BY [Date])
FROM PERS_Attendance
),
cte2 AS (
SELECT a.EmpID, b.[Date] As InDate, a.[Date] AS OutDate,
HoursWorked = DATEDIFF(hour, b.[Date], a.[Date])
FROM cte1 a
LEFT JOIN cte1 b ON a.EmpID = b.EmpID and a.rn = b.rn + 1
WHERE a.Funckey = 'EMPOUT'
)
SELECT EmpID,
TotalHours = SUM(HoursWorked)
FROM cte2
WHERE YEAR(InDate) = @Year
AND MONTH(InDate) = @Month
GROUP BY EmpID