【发布时间】:2015-03-17 20:53:47
【问题描述】:
我正在尝试为 HR 编写一份报告,按部门、员工和工作日期划分正常和加班时间,但我真的不知道如何开始。
以下创建一个临时表,其中包含一系列示例数据,这些数据反映了我将使用的实际数据。
-- 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 小时都是加班,这就是我的问题。
按部门和工作日期对 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 2016 年 2 月 26 日加起来最多 9.6 小时,或 8 个正常加班和 1.3 个加班。现在我需要将 1.3 小时的加班分配给 work_date 的第一个或最后一个部门,这个加班分配由班次代码决定,如果是“天”,则将加班分配给最后一个部门,否则分配给第一个部门那天。
这当然很复杂,因为员工可能在 A 部门工作 8.5 小时,然后转到 B 部门再工作一个小时。结果必须是 A 部门 8 小时 reg,A 部门 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
请注意,work_date '2015-02-26' 的员工 'W-C0001' 的 RegHours 列现在如何显示所有行的完整小时数在那个 work_date 中,除了减去 OT 的最后一个条目。与OTHours 列相同,该work_date 中的行为零,最后一行除外。
我现在可以使用 Shift 代码来确定是将 OT 分配到工作日的第一笔交易还是最后一笔交易。
直到现在我还没有使用过 OVER(PARTITION BY ),但能够从 TomCatt 的回答中弄清楚它们是如何工作的。
是否有一种更简洁的方法可以根据start_time 然后是我使用的CASE 表达式将 OT 分配给最后一个或第一个事务?
【问题讨论】:
-
您需要在查询中执行此操作吗?我确信可以做到,但在代码中似乎会更容易。
-
您如何确定哪个部门在当天排在第一位?是否存在您未包含的 ID,这意味着较早的 ID 较低?
-
@regeter,是的,对不起,我忘记了。有一个名为 start_time 的 DateTime 列可用于确定工作日中的第一个或最后一个部门。
-
@Jen R 是的,查询将提供 SSRS 报告。
-
@Paul Griffin 我已经尝试了一些事情,一个查询,我将员工、部门和工作日期的小时数汇总到一个临时表中。但是,当一名员工在不同部门进行多项交易时,我不知道如何将正常和加班与工作日分开。
标签: sql sql-server sql-server-2008-r2 sum