我有一个存储功能,可以提取所有员工的时钟信息。我正试图提出一份例外报告来审核午餐。我当前的查询一次构建所有信息1段。
SELECT ftc.lEmployeeID, ftc.sFirstName, ftc.sLastName, ftc.dtTimeIn,
ftc.dtTimeOut, ftc.TotalHours, ftc.PunchedIn, ftc.Edited
FROM dbo.fTimeCard(@StartDate, @EndDate, @DeptList,
@iActive, @EmployeeList) AS ftc
LEFT OUTER JOIN Employees AS e ON ftc.lEmployeeID = e.lEmployeeID
WHERE (ftc.TotalHours >= 0) AND (ftc.DID IS NOT NULL) OR
(ftc.DID IS NOT NULL) AND (ftc.dtTimeOut IS NULL)
此输出如下所示:
24 Bob bibby 8/2/2013 11:55:23 AM 8/2/2013 3:36:44 PM 3.68
24 bob bibby 8/2/2013 4:10:46 PM 8/2/2013 8:14:30 PM 4.07
39 rob blah 8/2/2013 8:01:57 AM 8/2/2013 5:01:40 PM 9.01
41 john doe 8/2/2013 10:09:58 AM 8/2/2013 1:33:38 PM 3.4
41 john doe 8/2/2013 1:55:56 PM 8/2/2013 6:10:15 PM 4.25
我需要查询才能做两件事。
1)每天将这些细分组合在一起。 2)在新列中报告“休息时间”
获得该信息后,我需要检查每个细分的小时数,并确保发生两件事。
1)如果他们共工作了6个小时,他们会休息30分钟吗? 2)如果他们休息一下,他们会休息一下吗? 30分钟。
你看到鲍勃在上午11点55分打了一拳,并在3点36分打了一顿午餐。他于4点10分午餐时冲了上来,并于8点14分拳打脚踢。他共工作了7.75小时,并且休息了34分钟。 他在这里很好。我不想报告例外
约翰总共工作了7.65个小时。然而,当他拳打脚踢时,他只吃了22分钟的午餐。我需要报告“吉姆只吃了22分钟的午餐”你也会看到抢劫工作了9个小时,没有休息。我需要报告“抢劫工作超过6小时并且没有休息”
我认为如果我可以完成对两个细分的分组。然后我可以处理报告方面。
的 的 *更新**
我更改了查询以尝试完成此操作。以下是我目前的疑问:
SELECT ftc.lEmployeeID, ftc.sFirstName, ftc.sLastName, ftc.TotalHours, DATEDIFF(mi, MIN(ftc.dtTimeOut), MAX(ftc.dtTimeIn)) AS Break_Time_Minutes
FROM dbo.fTimeCard(@StartDate, @EndDate, @DeptList, @iActive, @EmployeeList) AS ftc LEFT OUTER JOIN
Employees AS e ON ftc.lEmployeeID = e.lEmployeeID
WHERE (ftc.TotalHours >= 0) AND (ftc.DID IS NOT NULL) OR
(ftc.DID IS NOT NULL) AND (ftc.dtTimeOut IS NULL)
GROUP BY ftc.lEmployeeID, ftc.sFirstName, ftc.sLastName, ftc.TotalHours
我的输出目前看起来像这样:
24 Bob bibby 3.68 -221
24 bob bibby 4.07 -244
39 rob blah 0.05 -3
39 rob blah 2.63 -158
41 john doe 3.4 -204
41 john doe 4.25 -255
正如您所看到的那样,它并没有按日期组合细分,而Break_time显示的是负分钟。它也没有结合日子。鲍勃的时间应该在1行。并且显示7.75分钟的休息时间应该是34分钟。
答案 0 :(得分:4)
我相信如果你想要将两个时间结合起来,你需要将它们从组中取出并加上它们。根据结果,报告可以检查总小时数和休息时间。如果要标记它们,可以添加case语句。
SELECT ftc.lEmployeeID
,ftc.sFirstName
,ftc.sLastName
,SUM(ftc.TotalHours) AS TotalHours
,DATEDIFF(mi, MIN(ftc.dtTimeOut), MAX(ftc.dtTimeIn)) AS BreakTimeMinutes
FROM dbo.fTimeCard(@StartDate, @EndDate,
@DeptList, @iActive,@ EmployeeList) AS ftc
WHERE SUM(ftc.TotalHours) >= 0 AND (ftc.DID IS NOT NULL) OR
(ftc.DID IS NOT NULL) AND (ftc.dtTimeOut IS NULL)
GROUP BY ftc.lEmployeeID, ftc.sFirstName, ftc.sLastName
我在sql中进行了这个快速测试,它似乎按照你想要的方式工作。你有没有给小组添加一些东西?
declare @table table (emp_id int,name varchar(4), tin time,tout time);
insert into @table
VALUES (1,'d','8:30:00','11:35:00'),
(1,'d','13:00:00','17:00:00');
SELECT t.emp_id
,t.name
,SUM(DATEDIFF(mi, tin,tout))/60 as hours
,DATEDIFF(mi, MIN(tout), MAX(tin)) AS BreakTimeMinutes
FROM @table t
GROUP BY t.emp_id, t.name
答案 1 :(得分:2)
使用示例SQL的相关部分,我创建了一个SQL Fiddle,显示了如何完成此操作。您可以在此处查看:http://sqlfiddle.com/#!6/f05ce/3
SELECT EmployeeId, Num_Hours,
CASE WHEN tmp.Break_Time_Minutes < 0 Then 0 Else Break_Time_Minutes END As Break_Time_Minutes,
CASE WHEN tmp.Break_Time_Minutes < 0 Then 1 Else 0 END As SkippedBreak
FROM (
SELECT EmployeeId,
Round(SUM(DATEDIFF(second, TimeIn, TimeOut) / 60.0 / 60.0),1) As NUM_Hours,
DateDiff(mi, Min(TimeOut), Max(TimeIn)) As Break_Time_Minutes FROM Employee
GROUP BY EmployeeId, CAST(TimeIn As Date)
) as tmp WHERE tmp.Num_Hours > 6 AND Break_Time_Minutes < 30
答案 2 :(得分:2)
这是一个如何工作的例子。
我使用的独立逻辑不依赖于您自定义的功能,因为此处的社区无法访问它(包括它使用的数据)。
相反,我建立了你设法提供的“输出”的答案
这将自包含,因为没有对象依赖。
DECLARE @EmpClock Table
(
EmployeeID Int,
FirstName VarChar(50),
LastName VarChar(50),
PunchIn DateTime,
PunchOut DateTime
)
INSERT INTO @EmpClock
SELECT 24,'Bob','bibby','8/2/2013 11:55:23 AM','8/2/2013 3:36:44 PM' UNION
SELECT 24,'bob','bibby','8/2/2013 4:10:46 PM','8/2/2013 8:14:30 PM' UNION
SELECT 39,'rob','blah','8/2/2013 8:01:57 AM','8/2/2013 5:01:40 PM' UNION
SELECT 41,'john','doe','8/2/2013 10:09:58 AM','8/2/2013 1:33:38 PM' UNION
SELECT 41,'john','doe','8/2/2013 1:55:56 PM','8/2/2013 6:10:15 PM' UNION
SELECT 1,'Mike','TeeVee','8/2/2013 12:05:30 PM','8/2/2013 2:15:45 PM' UNION
SELECT 1,'Mike','TeeVee','8/2/2013 2:25:05 PM','8/2/2013 3:35:25 PM' UNION
SELECT 1,'Mike','TeeVee','8/2/2013 3:50:15 PM','8/2/2013 5:30:55 PM' UNION
SELECT 1,'Mike','TeeVee','8/2/2013 5:40:35 PM','8/2/2013 6:50:20 PM'
SELECT *,
DATEDIFF(SECOND, '', EC.WorkedTotal)/60.0/60.0[WorkedHours],
DATEDIFF(SECOND, '', EC.BreakTotal )/60.0 [BreakMinutes]
FROM
(
SELECT EC.*,
DATEADD(SECOND, DATEDIFF(SECOND, WorkedTotal, WorkPeriod), CAST('' as Time(0)))[BreakTotal]
FROM
(
SELECT EC.EmployeeID, EC.EmployeeName, EC.Day,
DATEADD(SECOND, DATEDIFF(SECOND, EC.FirstPunchIn, EC.LastPunchOut), CAST('' as Time(0)))[WorkPeriod],
DATEADD(SECOND, EC.Worked, CAST('' as Time(0)))[WorkedTotal]
FROM
(
SELECT EC.EmployeeID,
(EC.FirstName + ' ' + EC.LastName)[EmployeeName],
--"Day" Assumes Punches do not span across midnight.
-- If any do, then the day of the Punch-In will be used.
CAST(EC.PunchIn as Date)[Day],
SUM(DATEDIFF(SECOND, EC.PunchIn, EC.PunchOut))[Worked],
MIN(EC.PunchIn)[FirstPunchIn],
MAX(EC.PunchOut)[LastPunchOut]
FROM @EmpClock as EC
GROUP BY EC.EmployeeID, (EC.FirstName + ' ' + EC.LastName), CAST(EC.PunchIn as Date)
) AS EC
) AS EC
) AS EC
WHERE EC.BreakTotal > DATEADD(MINUTE, 30, CAST('' as Time(0)))
AND EC.WorkedTotal > DATEADD(HOUR, 6, CAST('' as Time(0)))
注意我为名为“ Mike TeeVee ”的虚构员工添加了更多数据。
我是这样做的,以防你有一个员工休息休息或因任何原因需要紧急休息
这允许我们测试逻辑如何处理这些情况。
如果没有Where-Clause,我们会看到:
按原样运行(使用Where-Clause),您将看到它正确过滤掉结果:
您会注意到我将结果的Time-DataType格式显示为“ WorkedTotal ”和“ BreakTotal ”。我更喜欢这个用于显示目的,因为我们通常不会将小时视为整体100%的分数,而是将小时和剩余分数视为分钟。
我继续将分数小时和分数分钟分别包括为“ WorkedHours ”和“ BreakMinutes ”,以防您的要求需要以此格式进一步计算
“ WorkPeriod ”表示班次的持续时间(包括休息时间)。
我确定你不需要这些信息,但我把它包括在内是为了完整。
从这里的其他答案来看,我发现你在将你的逻辑融入他们的答案时遇到了问题,所以我也为你做了这个。
以下脚本只能在您的环境中运行:
SELECT *,
DATEDIFF(SECOND, '', EC.WorkedTotal)/60.0/60.0[WorkedHours],
DATEDIFF(SECOND, '', EC.BreakTotal )/60.0 [BreakMinutes]
FROM
(
SELECT EC.*,
DATEADD(SECOND, DATEDIFF(SECOND, WorkedTotal, WorkPeriod), CAST('' as Time(0)))[BreakTotal]
FROM
(
SELECT EC.EmployeeID, EC.EmployeeName, EC.Day,
DATEADD(SECOND, DATEDIFF(SECOND, EC.FirstPunchIn, EC.LastPunchOut), CAST('' as Time(0)))[WorkPeriod],
DATEADD(SECOND, EC.Worked, CAST('' as Time(0)))[WorkedTotal]
FROM
(
SELECT EC.EmployeeID,
(EC.FirstName + ' ' + EC.LastName)[EmployeeName],
--"Day" Assumes Punches do not span across midnight.
CAST(EC.PunchIn as Date)[Day],
SUM(DATEDIFF(SECOND, EC.PunchIn, EC.PunchOut))[Worked],
MIN(EC.PunchIn)[FirstPunchIn],
MAX(EC.PunchOut)[LastPunchOut]
FROM
( --I replaced my table variable @EmpClock with a call to your own logic.
SELECT EC.lEmployeeID[EmployeeID], EC.sFirstName[FirstName], EC.sLastName[LastName],
EC.dtTimeIn[PunchIn], EC.dtTimeOut[PunchOut]
--You have these in your original query, but the values are missing in your "output".
--,EC.TotalHours, EC.PunchedIn, EC.Edited
FROM dbo.fTimeCard(@StartDate, @EndDate, @DeptList, @iActive, @EmployeeList) as EC
LEFT JOIN Employees as E
ON EC.lEmployeeID = E.lEmployeeID
) AS EC
GROUP BY EC.EmployeeID, (EC.FirstName + ' ' + EC.LastName), CAST(EC.PunchIn as Date)
) AS EC
) AS EC
) AS EC
WHERE EC.BreakTotal > DATEADD(MINUTE, 30, CAST('' as Time(0)))
AND EC.WorkedTotal > DATEADD(HOUR, 6, CAST('' as Time(0)))
我还注意到你的where子句有问题:
WHERE (ftc.TotalHours >= 0) AND (ftc.DID IS NOT NULL) OR
(ftc.DID IS NOT NULL) AND (ftc.dtTimeOut IS NULL)
目前还不清楚你在这里尝试“ OR ”是什么,并且你有两次列出“ ftc.DID IS NOT NULL ”。
您可能希望查看该逻辑,并在使用OR时考虑正确使用括号
由于这种混淆,我在上面的例子中省略了这个逻辑。
你偶然的意思是这个吗?:
WHERE ftc.DID IS NOT NULL
AND (ftc.TotalHours >= 0 OR ftc.dtTimeOut IS NULL)
答案 3 :(得分:1)
试试这个: -
SELECT ftc.lEmployeeID,sum(ftc.TotalHours)as TotalHours,
ABS(DATEDIFF(mi, MIN(convert(datetime,ftc.dtTimeOut,9)), MAX(convert(datetime,ftc.dtTimeIn,9)))) AS Break_Time_Minutes
FROM dbo.fTimeCard(@StartDate, @EndDate, @DeptList, @iActive, @EmployeeList) AS ftc
LEFT OUTER JOIN
Employees AS e ON ftc.lEmployeeID = e.lEmployeeID
WHERE (ftc.TotalHours >= 0) AND (ftc.DID IS NOT NULL) OR
(ftc.DID IS NOT NULL) AND (ftc.dtTimeOut IS NULL)
group by ftc.EmployeeiD,DATE(ftc.dtTimeIn)
答案 4 :(得分:1)
很抱歉写在这里。内容可能很长。 你的问题仍然没有解决,但它似乎很容易,因为你强加给我们 dbo.fTimeCard(@StartDate,@ EndDate,@ DeptList,@ iActive,@ EmployeeList)。 我们不知道ftc.TotalHours是否已经是某事物的总和或它是什么。 你要做的只是在表变量中显示表结构和一些数据。你的解释是很好的enuf。 其次我想指出的是,你永远不会通过id,firstname,lastname 来做分组。 group by id绰绰有余。所以在你的情况下你需要CTE.i不能写整个查询因为某些东西是表格而且数据不清楚。
;with CTE as
(SELECT ftc.lEmployeeID, ftc.sFirstName, ftc.sLastName, ftc.dtTimeIn, ftc.dtTimeOut, ftc.TotalHours, ftc.PunchedIn, ftc.Edited
FROM dbo.fTimeCard(@StartDate, @EndDate, @DeptList, @iActive, @EmployeeList) AS ftc LEFT OUTER JOIN
Employees AS e ON ftc.lEmployeeID = e.lEmployeeID
WHERE (ftc.TotalHours >= 0) AND (ftc.DID IS NOT NULL) OR
(ftc.DID IS NOT NULL) AND (ftc.dtTimeOut IS NULL)
)
,CTE1 as
(
SELECT ftc.lEmployeeID
,SUM(ftc.TotalHours) AS TotalHours
MIN(ftc.dtTimeOut) MindtTimeOut, MAX(ftc.dtTimeIn) AS MAXdtTimeIn
FROM dbo.fTimeCard(@StartDate, @EndDate, @DeptList, @iActive, @EmployeeList) AS ftc
WHERE SUM(ftc.TotalHours) >= 0 AND (ftc.DID IS NOT NULL) OR
(ftc.DID IS NOT NULL) AND (ftc.dtTimeOut IS NULL)
GROUP BY ftc.lEmployeeID
)
,CTE2 as
(
join cte and cte1 on employeeid--well it depend
)
select * from cte2--this is just indicative.
答案 5 :(得分:1)
使用CTE对分段进行分组,您可以查询此CTE以检查中断持续时间。
在最终的CASE WHEN
语句中添加其他检查:
; with
DailyRecords as
( -- add a rownumber to each entry by employee/day
select ROW_NUMBER() over (partition by ftc.lEmployeeId, cast(ftc.dtTimeIn as date)
order by ftc.lEmployeeId, ftc.dtTimeIn ) as rownum,
cast(ftc.dtTimeIn as date) as [Day],
ftc.lEmployeeID, ftc.sFirstName, ftc.sLastName, ftc.dtTimeIn, ftc.dtTimeOut, ftc.TotalHours
from fTimeCard(@StartDate, @EndDate, @DeptList, @iActive, @EmployeeList) ftc
),
DailyRequest as
( -- group 2 segments together (rownum 1 and 2), and report break time in a new column
select Segment1.lEmployeeId, Segment1.sFirstName, Segment1.sLastName,
Segment1.[Day],
coalesce(round(datediff(mi,Segment1.dtTimeIn, Segment1.dtTimeOut) / 60.0, 2),0) as Duration1,
coalesce(round(datediff(mi,Segment2.dtTimeIn, Segment2.dtTimeOut) / 60.0, 2), 0) as Duration2,
coalesce(round(datediff(mi,Segment1.dtTimeOut,Segment2.dtTimeIn),2),0) as BreakDuration
from DailyRecords Segment1
left join DailyRecords Segment2
on segment1.lEmployeeID = Segment2.lEmployeeID
and Segment1.[Day] = Segment2.[Day]
and Segment2.rownum = 2
where Segment1.rowNum= 1
)
-- make report from DailyRequest with remarks
select lEmployeeId, sFirstName, sLastName, [Day], Duration1, Duration2, BreakDuration,
case
when Duration1+Duration2 >= 6 and BreakDuration = 0 then 'No Break'
when Duration1+Duration2 >= 6 and BreakDuration < 30 then ltrim(str(coalesce(BreakDuration, 0))) +' mn break'
when Duration1+Duration2 >= 6 and BreakDuration > 35 then ltrim(str(coalesce(BreakDuration, 0))) +' mn break'
end as Remarks
from DailyRequest D
如果需要,添加LEFT OUTER JOIN Employee
(在您的要求中没有使用它,我将其删除)
答案 6 :(得分:0)
确定休息时间的快捷方式,是最后一次退房和第一次办理登机手续,然后您有时间总计。它与ftc.TotalHours之间的差异是所用的休息时间。然后你可以添加一些代码来报告异常。 如果选择了多天,则下面的代码将不起作用,但是我们需要的只是一个日期变量,并将日期变量添加到GROUP BY子句中。
SELECT out.lEmployeeID, out.sFirstName, out.sLastName,
CASE WHEN (out.TotalHours > 6 AND out.Break_Time_Minutes < 30) THEN
'report exception' ELSE 0 END AS Exception_Status
FROM
( SELECT ftc.lEmployeeID, ftc.sFirstName, ftc.sLastName,
ABS(DATEDIFF(mi, MAX(ftc.dtTimeOut), MIN(ftc.dtTimeIn))) - SUM(ftc.TotalHours) * 60
AS Break_Time_Minutes, SUM(ftc.TotalHours) AS TotalHours
FROM dbo.fTimeCard(@StartDate, @EndDate, @DeptList, @iActive, @EmployeeList) AS ftc
LEFT OUTER JOIN Employees AS e ON ftc.lEmployeeID = e.lEmployeeID
WHERE (ftc.TotalHours >= 0) AND (ftc.DID IS NOT NULL) OR
(ftc.DID IS NOT NULL) AND (ftc.dtTimeOut IS NULL)
GROUP BY ftc.lEmployeeID) as out