我正在尝试为人力资源部门编写一份报告,按部门,员工和工作日分割常规和加班时间,我真的不确定如何开始。
以下内容创建一个临时表,其中包含一系列样本数据,这些数据反映了我将使用的实际数据。
-- Create Temp Tables --
CREATE TABLE #Employee_Hours
(
emp_num nvarchar(20)
,dept nvarchar(20)
,shift nvarchar(3)
,work_date Date
,start_time DateTime
,hrs Decimal(19,8)
);
-- Populate #Employee_Hours with sample hours
INSERT #Employee_Hours(emp_num, dept, shift, work_date, start_time, hrs)
VALUES ('W-C0001', 'Admin', 'Day', '2015-02-22', '2015-02-22 06:30:00.000', 7.9)
,('W-C0001', 'Admin', 'Day', '2015-02-24', '2015-02-24 06:45:00.000', 8.6)
,('W-C0001', 'Admin', 'Day', '2015-02-26', '2015-02-26 08:00:00.000', 2.0)
,('W-C0001', 'Admin', 'Day', '2015-02-26', '2015-02-26 10:00:00.000', 0.5)
,('W-C0001', 'Asmb', 'Day', '2015-02-26', '2015-02-26 11:00:00.000', 1.33333)
,('W-C0001', 'Shop A', 'Day', '2015-02-26', '2015-02-26 11:30:00.000', 5.5)
,('G-C0000', 'Admin', 'Day', '2015-02-22', '2015-02-22 08:00:00.000', 4.5)
,('G-C0000', 'Admin', 'Day', '2015-02-22', '2015-02-22 12:30:00.000', 4.0)
,('G-C0000', 'Admin', 'Day', '2015-02-23', '2015-02-23 07:30:00.000', 8.25)
,('G-C0000', 'Admin', 'Day', '2015-02-24', '2015-02-24 08:30:00.000', 8.9)
,('W-R0000', 'Eng', 'Day', '2015-02-22', '2015-02-22 08:30:00.000', 8.02)
,('W-R0000', 'Eng', 'Day', '2015-02-24', '2015-02-24 09:30:00.000', 8.75)
GO
SELECT * FROM #Employee_Hours
DROP TABLE #Employee_Hours
数据表有五列:
emp_num
- 员工编号dept
- 交易部门shift
- 交易转移work_date
- 交易日期start_time
- 事务开始时间hrs
- 每笔交易的总小时数现在,我们的一些员工将在一天之内更换部门,当他们从旧部门退出时,然后再回到不同的部门。
请注意emp_num'W-C0001'的交易,该员工在work_date 02/26/2015上有四笔交易。我们的加班是每个工作日加班8小时后的任何事情,这就是我的问题。
由department和work_date总结为emp_num'W-C0001',样本数据如下所示:
work_date | dept | hrs
02/26/2015 Admin 2.5
02/26/2015 Asmb 1.3
02/26/2015 Shop A 5.5
在work_date 02/26/2016,或8个常规和1.3加时,这相当于9.6小时。现在我需要将1.3小时的加班时间分配给work_date的第一个或最后一个部门,这个加班分配由班次代码确定,如果是'Day'则将加班分配给最后一个部门,否则分配到第一个部门。那一天。
这当然很复杂,因为员工可能在A部门工作8.5小时,然后再转到B部门再工作一小时。结果必须是部门A的8小时注册,部门B的加班时间为0.5小时,B部门的加班时间为1小时。
我使用上面的示例从最终选择语句中得到的结果是:
work_date | dept | hrs_reg | hrs_ot
02/26/2015 Admin 2.5 0
02/26/2015 Asmb 1.3 0
02/26/2015 Shop A 4.2 1.3
更新
我想我已经得到了@TommCatt。我正在使用start_time
列来帮助决定哪个部门加班。我将它添加到临时表中的示例数据中。
我略微修改了他的答案,它似乎给了我想要的东西。我添加了start_time
列,并在CASE
表达式中将其用于将加班时间分配给MAX(start_time)
。
WITH EmpOvertime
(
emp_num
,dept
,shift
,work_date
,start_time
,hrs
,DailyTotal
,OTHours
)
AS
(
SELECT h.emp_num
,h.dept
,h.shift
,h.work_date
,h.start_time
,h.hrs
,SUM(h.hrs) OVER(PARTITION BY h.emp_num, h.work_date)
,CASE
WHEN Sum(h.hrs) OVER(PARTITION BY h.emp_num, h.work_date) - 8 > 0
THEN Sum(h.hrs) OVER(PARTITION BY h.emp_num, h.work_date) - 8
ELSE 0
END
FROM #Employee_Hours AS h
)
SELECT emp_num
,dept
,shift
,work_date
,hrs
,DailyTotal
,CASE -- Only subract overtime from the last entry for the work day
WHEN start_time = (SELECT MAX(h2.start_time)
FROM #Employee_Hours AS h2
WHERE h2.emp_num = ot.emp_num
AND h2.work_date = ot.work_date)
THEN hrs - OTHours
ELSE hrs
END AS RegHours
,CASE -- Only allocate overtime to the last entry for the work day
WHEN start_time = (SELECT MAX(h2.start_time)
FROM #Employee_Hours AS h2
WHERE h2.emp_num = ot.emp_num
AND h2.work_date = ot.work_date)
THEN OTHours
ELSE 0
END AS OTHours
FROM EmpOvertime AS ot
ORDER BY ot.emp_num, ot.work_date;
结果如下:
emp_num dept shift work_date hrs DailyTotal RegHours OTHours
------- ----- ---- ---------- --- --- --- ---
G-C0000 Admin Day 2015-02-22 4.5 8.5 4.5 0.0
G-C0000 Admin Day 2015-02-22 4.0 8.5 3.5 0.5
W-C0001 Admin Day 2015-02-22 7.9 7.9 7.9 0.0
W-C0001 Admin Day 2015-02-24 8.6 8.6 8.0 0.6
W-C0001 Admin Day 2015-02-26 2.0 9.3 2.0 0.0
W-C0001 Admin Day 2015-02-26 0.5 9.3 0.5 0.0
W-C0001 Asmb Day 2015-02-26 1.3 9.3 1.3 0.0
W-C0001 Shop A Day 2015-02-26 5.5 9.3 4.1 1.3
请注意,RegHours
'2015-02-26'的员工'W-C0001'的work_date
列现在显示完整该work_date中所有行的小时数,除了它减去OT的最后一个条目。与OTHours
列相同,该work_date中的行为零,除了最后一行。
我现在可以使用Shift代码来确定是否将OT分配给工作日的第一个或最后一个事务。
直到现在我还没有使用过OVER(PARTITION BY )
,但是能够从TommCatt的回答中找出它们是如何工作的。
是否有更简洁的方法可以根据start_time
以及我正在使用的CASE
表达式将OT分配到最后一次或第一次交易?
答案 0 :(得分:1)
丑陋,不会一直有效,但有类似的东西:
SELECT work_date,
dept,
CASE WHEN ot.tot IS NOT NULL THEN total - ot.tot ELSE total END AS s,
ot.tot
FROM (
SELECT eh.work_date,
eh.dept,
eo.d,
SUM(eh.hrs) total,
eo.t
FROM #Employee_Hours eh
JOIN (
SELECT work_date, SUM(hrs) t, MAX(dept) d
FROM #Employee_Hours
GROUP BY work_date
) eo
ON eo.work_date = eh.work_date
WHERE eh.work_date = '2015-02-26'
GROUP BY eh.work_date, eh.dept, eo.d, eo.t
) a
CROSS APPLY (SELECT CASE WHEN t <= 8 THEN 0 ELSE CASE WHEN d = dept THEN t-8 END END tot) AS ot
答案 1 :(得分:1)
如果您不了解分析,我不确定我是否可以向您解释。我对数据库概念非常了解,但是在他们最终开始沉沦之前,我只需要一次又一次地使用分析。我仍然不喜欢使用它们,因为虽然它们通常很方便,但它们需要 sooo 我们进行了大量测试,以确保您得到正确答案。
这个想法是,您必须在每个特定日期为每位员工运行总计小时数。然后有一个列忽略每天运行的总计,直到它超过8小时。将其反馈到另一个查询中,该查询将小时分为常规和加班。
with
EmpOvertime( emp_num, dept, shift, work_date, hrs, RunningHrs, OTHours )as(
select h.emp_num, h.dept, h.shift, h.work_date, h.hrs,
Sum( h.hrs )over( partition by h.emp_num, h.work_date order by h.hrs ),
case when Sum( h.hrs )over( partition by h.emp_num, h.work_date order by h.hrs ) - 8 > 0
then Sum( h.hrs )over( partition by h.emp_num, h.work_date order by h.hrs ) - 8
else 0
end
from Employee_Hours h
)
select emp_num, dept, shift, work_date, hrs ClockedHrs, RunningHrs, hrs - OTHours RegHours, OTHours
from EmpOvertime ot
order by ot.emp_num, ot.work_date;
我已将CTE中Sum
的原始总数暴露为RunningHrs,因此您可以直接看到生成的值。一旦您对查询感到满意,就可以将其删除,因为它不会直接用于最终查询。
这并不是您要求的,但您没有指明任何方式来指定班次的第一个或最后一个周期。然后无法在查询中使用它。因此,加班时间只显示在每日总开始超过8小时的时间段内。我希望这足以让你开始。