终于不用加班了!

这个星期一下午下班的时候,我问项目经理我还要不要继续加班的时候,项目经理说如果我的手头上的工作不是很多而且又不赶的话,可以不用加班,可以早点回家.因为近来加班也实在加得很辛苦了!

这天下午五点半下班了我就走了,可是去到公车站才发现等公车的人是那么的多,也许是因为前段时间都要加班加到七点多八点,回去的时候人都比较少了的缘故吧,感觉那天等公车的人是特别特别的多,等了有二十分钟左右,终于等来了一辆经过我住的附近的那个公车站的,挤了一车厢人的,有点像货车的公车!!好不容易挤了上去(真有点庆幸自己有那么“苗条”的身材,要不然就挤不了上去的了!呵呵。。)平时是四十分钟就到的,可那天老天(也许是公车司机吧)就是不给我面子,偏要我坐了一个多小时才让我回到。!站到我都差不多晕倒了!!

吃完饭回到房间里还不到八点钟,感觉怪怪的,因为平时这个时候应该还是在公司里或在公车上的!所以很不习惯,不知道做些什么好!很无聊地在INTERNET上“忙碌”了一个晚上。等洗完澡了,躺在床上,我想了一下,感觉今晚不知道为什么就是过得有点颓废.不禁也想一下:以前加班都不会觉得这样的颓废的,是不是因为今天不用加班了,下班得太早回来了的缘故呢?

为了证实一下加班与不加班对我的影响,决定第二天一下班就回来,可惜的是,感觉和第一天的差不多,都是郁闷的!也许是自己能掌握的时间太多了,但又没有找到一个比较好的方法来度过,所以会觉得这样吧!

现在真的很郁闷,我都不了解我自己,到底是希望加班还是不加班呢?到底是加班好还是不加班好呢?加班了会肯定不开心吗?不加班了我肯定会开心吗??好像也不见得是一定的哦!我有时候在想,我自己是不是有点犯贱呢??加班的时候天天叫苦,不加班了又晚晚郁闷!!!呵呵~~~~~~~人(特别是我自己),真的很难去捉摸,去猜透!

<think> 我们已知:用户需要计算工作日的加班时长,规则如下: 1. 只考虑工作日(周末不考虑,但当前查询已经限定工作日) 2. 基础加班时长 = 实际在岗时长 - 上午、中午、下午休息时段的扣除 3. 净加班时长 = 基础加班时长 - 8小时(即扣除标准工作时长) 4. 如果净加班时长为负数,则不统计(即取0) 根据之前的逻辑,我们已经在在岗时长中扣除了三个休息时段。现在要求在此基础上再减去8小时(标准工作时间),并且当结果为负时取0。 因此,我们需要调整原来的计算: total_hours = (在岗时长 - 休息时段扣除) - 8 如果total_hours < 0,则取0 但是注意:原查询已经计算了扣除休息时段后的在岗时长(即基础加班时长),现在需要在此基础上减去8,并处理负数。 原查询计算的是单条记录的时长(扣除休息时段),然后按用户分组求和。但这里有一个问题:我们是否应该先对每条记录计算扣除休息时段后的时长,再减去8小时?但注意,8小时是标准工作时间,而加班计算通常是在一个工作日内,所以应该是每条记录(代表一天)计算后减去8小时,然后取非负值,最后再按用户汇总。 所以步骤: 1. 对每条记录,计算扣除休息时段后的在岗时长(即原查询中的计算,但不要按用户分组,而是先计算每条记录的时长) 2. 然后,将该时长减去8小时,如果结果小于0则取0(即一天加班时长=MAX(0, 扣除休息时段后的时长-8)) 3. 最后按用户分组求和 因此,我们需要先构建子查询,计算每条记录的加班时长(已经扣除休息时段并减去8小时后的非负值),然后按用户分组求和。 修改后的SQL如下: 注意:原查询中的休息时段扣除逻辑保持不变(上午、中午、下午三个时段) 我们将原查询中的计算部分单独拿出来作为每条记录的基础时长(扣除休息时段),然后减去8,再取非负,最后分组求和。 由于原查询中已经计算了扣除休息时段后的时长(但原查询是直接按用户分组求和,没有先减8),现在需要调整。 修改步骤: 1. 将原查询中的计算部分作为子查询的内层,得到每条记录扣除休息时段后的时长(称为daily_hours) 2. 然后计算每日加班时长:GREATEST(0, daily_hours - 8) 3. 最后按用户分组求和,并保留两位小数 注意:原查询中的休息时段扣除逻辑中,使用了TRUNC(TO_DATE(starttime, ...))来获取日期部分。这里假设每条记录都是同一天的(即没有跨天记录)。如果有跨天记录,则需要另外处理,但用户需求是工作加班,通常不跨天。 因此,SQL可以这样写: ```sql SELECT userid, username, ROUND(SUM(daily_overtime), 2) AS total_overtime_hours FROM ( SELECT userid, username, -- 计算扣除休息时段后的每日在岗时长 ( (TO_DATE(endtime, 'YYYY-MM-DD HH24:MI:SS') - TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) * 24 - ( -- 早餐重叠时长(小时) GREATEST(0, LEAST(TO_DATE(endtime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + (8/24) + (45/24/60)) - GREATEST(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + (8/24) + (15/24/60)) ) * 24 + -- 午餐重叠时长(小时) GREATEST(0, LEAST(TO_DATE(endtime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + (12/24) + (30/24/60)) - GREATEST(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + (12/24)) ) * 24 + -- 晚餐重叠时长(小时) GREATEST(0, LEAST(TO_DATE(endtime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + (17/24) + (45/24/60)) - GREATEST(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + (17/24) + (15/24/60)) ) * 24 ) ) AS daily_hours FROM work_record -- 假设有一个字段is_weekday来标记工作日,或者根据日期判断,但用户要求只考虑工作日且不考虑周末,所以这里需要过滤 WHERE ... -- 这里添加条件过滤工作日,例如:TO_CHAR(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS'), 'D') NOT IN (6,7) 表示排除周六周日(根据数据库设置,周日为1,周六为6,周日为7?) ) -- 然后计算每日加班时长(扣除8小时,负数取0) , daily_overtime AS ( SELECT userid, username, GREATEST(0, daily_hours - 8) AS daily_overtime FROM daily_data ) -- 最后按用户分组求和 GROUP BY userid, username; ``` 但是,由于用户没有提供如何判断工作日的条件,这里假设有一个条件可以过滤工作日。如果不知道如何判断,可能需要用户提供。 另外,注意原查询中使用了TRUNC(TO_DATE(starttime, ...))来获取日期部分,这里要求每条记录都是同一天的。如果一条记录跨天,则计算结果可能不准确。 由于用户要求只统计工作日,所以我们在子查询中先过滤工作日记录。 另外,我们还需要注意:原查询中使用了starttime来截断日期,如果一条记录跨天,则使用starttime的日期可能不准确(比如从今天工作到明天),但通常加班记录不会跨天,所以暂时这样处理。 优化:我们可以使用TRUNC(starttime)来获取日期,但这里已经用了TO_DATE转换,所以直接使用TRUNC(TO_DATE(starttime, ...))。 因此,最终SQL如下(假设使用TO_CHAR判断工作日,排除周六周日): ```sql SELECT userid, username, ROUND(SUM( GREATEST(0, daily_hours - 8) ), 2) AS total_overtime_hours FROM ( SELECT userid, username, ( (TO_DATE(endtime, 'YYYY-MM-DD HH24:MI:SS') - TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) * 24 - ( -- 早餐重叠时长(小时) GREATEST(0, LEAST(TO_DATE(endtime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + (8/24) + (45/24/60)) - GREATEST(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + (8/24) + (15/24/60)) ) * 24 + -- 午餐重叠时长(小时) GREATEST(0, LEAST(TO_DATE(endtime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + (12/24) + (30/24/60)) - GREATEST(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + (12/24)) ) * 24 + -- 晚餐重叠时长(小时) GREATEST(0, LEAST(TO_DATE(endtime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + (17/24) + (45/24/60)) - GREATEST(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + (17/24) + (15/24/60)) ) * 24 ) ) AS daily_hours FROM work_record WHERE TO_CHAR(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS'), 'D') NOT IN ('6','7') -- 排除周六和周日(根据实际设置调整,有些系统周日是1,周六是7,这里用'6'和'7'代表周六和周日) ) t GROUP BY userid, username; ``` 注意:这里的工作日判断可能因数据库设置不同而不同,例如,有些地区把周一定为1,周日定为7,而有些地区把周日定为1,周六定为7。所以需要根据实际情况调整。 另外,如果用户有节假日表,还需要排除节假日。但用户当前需求没有提到节假日,所以暂时只排除周末。 如果用户需要排除节假日,那么需要关联节假日表,这里不展开。 最后,我们按用户分组,将每日加班时长(已经减去8小时且负数取0)求和,并四舍五入到两位小数。 但是,注意:原查询中休息时段的扣除逻辑中,上午休息时段是8:15到8:45,午餐12:00到12:30,下午17:15到17:45。这些时段是固定的,所以计算重叠时间的方法是正确的。 另外,如果用户的一条记录并没有覆盖整个休息时段,那么重叠计算会自动取实际重叠部分。 因此,这个SQL应该能够满足需求。 最后,如果用户需要考虑跨天的情况,那么需要重新设计,但这里假设不跨天。 如果存在跨天记录,那么使用TRUNC(TO_DATE(starttime,...))获取的日期就不准确了(因为endtime可能到了第二天),此时应该用两个日期分别判断,但用户需求是工作日,跨天记录可能很少,而且通常加班不会跨天,所以暂时不考虑。 如果确实有跨天记录,建议将一条记录拆分成两天分别计算,但这样会复杂很多。 综上所述,我们给出上述SQL。 </think>### 工作加班时长统计方案 根据需求,工作加班时长的计算逻辑如下: 1. **只统计工作日**(排除周末) 2. **基础加班时长** = 实际在岗时长 - 休息时段(上午、中午、下午) 3. **净加班时长** = 基础加班时长 - 8小时(标准工作时长) 4. **负数处理**:当净加班时长 ≤ 0 时不统计(取0) 以下是完整的SQL实现方案: ```sql SELECT userid, username, ROUND(SUM( GREATEST(0, -- 基础在岗时长(小时) (TO_DATE(endtime, 'YYYY-MM-DD HH24:MI:SS') - TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) * 24 -- 扣除上午休息时段(08:15-08:45) - GREATEST(0, LEAST(TO_DATE(endtime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + 8/24 + 45/24/60 ) - GREATEST(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + 8/24 + 15/24/60 ) ) * 24 -- 扣除中午休息时段(12:00-12:30) - GREATEST(0, LEAST(TO_DATE(endtime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + 12/24 + 30/24/60 ) - GREATEST(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + 12/24 ) ) * 24 -- 扣除下午休息时段(17:15-17:45) - GREATEST(0, LEAST(TO_DATE(endtime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + 17/24 + 45/24/60 ) - GREATEST(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS'), TRUNC(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS')) + 17/24 + 15/24/60 ) ) * 24 - 8 -- 减去标准工作时长8小时 ) ), 2) AS overtime_hours FROM work_record -- 排除周末(1=周日,7=周六,根据实际NLS设置调整) WHERE TO_CHAR(TO_DATE(starttime, 'YYYY-MM-DD HH24:MI:SS'), 'D') NOT IN ('1','7') GROUP BY userid, username; ``` ### 关键计算逻辑说明 1. **休息时段扣除公式**: - 重叠时长 = $MAX(0, MIN(结束时间, 休息结束) - MAX(开始时间, 休息开始))$ - 上午休息:$休息开始 = TRUNC(日期) + \frac{8}{24} + \frac{15}{24×60}$ - 上午结束:$休息结束 = TRUNC(日期) + \frac{8}{24} + \frac{45}{24×60}$ 2. **净加班时长计算**: $$ \text{净加班时长} = \begin{cases} (\text{实际时长} - \sum{\text{休息重叠时长}} - 8) & \text{当结果} > 0 \\ 0 & \text{当结果} \leq 0 \end{cases} $$ 3. **日期处理技巧**: - `TRUNC(date)`:截取日期部分(去除时间) - 时间计算:$\frac{小时}{24} + \frac{分钟}{24×60}$(转换为天的小数) - `TO_CHAR(..., 'D')`:获取星期几(1=周日,7=周六) ### 执行效果示例 | USERID | USERNAME | OVERTIME_HOURS | |--------|----------|----------------| | 1001 | 张三 | 10.5 | | 1002 | 李四 | 3.25 | | 1003 | 王五 | 0.00 | <!-- 净加班≤0 --> ### 注意事项 1. **日期格式验证**: ```sql -- 检查实际数据格式 SELECT starttime FROM work_record WHERE ROWNUM = 1; ``` 确保格式与`'YYYY-MM-DD HH24:MI:SS'`完全匹配[^1] 2. **跨天记录处理**: ```sql -- 添加跨天判断 CASE WHEN TRUNC(starttime) < TRUNC(endtime) THEN 24*(TRUNC(endtime)-TRUNC(starttime)-1) ELSE 0 END ``` 3. **性能优化建议**: ```sql -- 创建函数索引 CREATE INDEX idx_workdate ON work_record(TRUNC(TO_DATE(starttime))); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值