以递增顺序求和,按总和限制

时间:2014-06-03 12:24:39

标签: sql oracle row

我正在尝试使用SQL来缩短工作时间,但我不能总结它。总和需要按升序排列,当达到边界极限时,它会继续在另一列或行中求和。

我的数据是这样的:

Employee_Id |    Date     | Overtime_Day | Overtime_Night
48          |  05/03/2014 |   3 hours    |   4 hours
48          |  05/04/2014 |   9 hours    |   1 hours
48          |  05/10/2014 |   1 hours    |   1 hours
48          |  05/20/2014 |   9 hours    |   4 hours
            |             |= 22 hours    | = 10 hours

SUM的边界是这样的:

Overtime (Day+Night) 50%: from 0 to 15
Overtime (Day+Night) 100%: > 15

但我也需要知道它是白天还是黑夜。

Overtime 50% is all hours from 0 to 15, no matter if it's day or night:
3 hours [05/03/2014] [Day]
4 hours [05/03/2014] [Night]
8 hours [05/04/2014] [Day] (2 hours is left for this day, left hours and next hours will be 100%)
Overtime 100% is all hours above 15:
1 hours [05/04/2014] [Day]
1 hours [05/04/2014] [Night]
1 hours [05/10/2014] [Day]
1 hours [05/10/2014] [Night]
9 hours [05/20/2014] [Day]
4 hours [05/20/2014] [Night]

然后我有:

Overtime 50% day: 11 hrs
Overtime 50% night: 4 hrs
Overtime 100% day: 11 hrs
Overtime 100% night: 6 hrs

我想要的结果

Employee_Id | Overtime_Day_50% | Overtime_Night_50% | Overtime_Day_100% | Overtime_Night_100% 
48          |   11 hours       |   4 hours          |   11 hrs          |    6 hrs

或者

Employee_Id |       Type           |   Hours    
48          |  Overtime_Day_50%    |   11 hours
48          |  Overtime_Night_50%  |   4 hours
48          |  Overtime_Day_100%   |   11 hours
48          |  Overtime_Night_100% |   6 hours

(行方式或列方式无关紧要)

很抱歉,如果我没有说清楚,我真的不知道是否可以用SQL来完成这个计算。

任何想法都将不胜感激。 谢谢!


修改

我有这个SELECT,我可以得到所有加班50%和所有加班100%,但有了这个我不能分开什么是白天和什么是夜班。

WITH DATA AS (           
SELECT 48 AS EMPLOYEE_ID, '05/03/2014' AS "DATE", 3 AS OVERTIME_DAY, 4 AS OVERTIME_NIGHT FROM DUAL UNION
SELECT 48 AS EMPLOYEE_ID, '05/04/2014' AS "DATE", 9 AS OVERTIME_DAY, 1 AS OVERTIME_NIGHT FROM DUAL UNION
SELECT 48 AS EMPLOYEE_ID, '05/10/2014' AS "DATE", 1 AS OVERTIME_DAY, 1 AS OVERTIME_NIGHT FROM DUAL UNION
SELECT 48 AS EMPLOYEE_ID, '05/20/2014' AS "DATE", 9 AS OVERTIME_DAY, 4 AS OVERTIME_NIGHT FROM DUAL
)
SELECT
    EMPLOYEE_ID,
    CASE WHEN SUM(OVERTIME_DAY + OVERTIME_NIGHT) >= 15 THEN
      15
    ELSE
      SUM(OVERTIME_DAY + OVERTIME_NIGHT)
    END AS OVERTIME_50,

    GREATEST(0,SUM(OVERTIME_DAY + OVERTIME_NIGHT) - 15) AS OVERTIME_100

FROM DATA
GROUP BY EMPLOYEE_ID

结果:

Employeed_Id   |   OVERTIME_50    |   OVERTIME_100
    48         |        15        |         17

2 个答案:

答案 0 :(得分:1)

如果无论白天或晚上计算加班时间,为什么不预先计算每组(白天/黑夜)和总计每日总计的总计。无论如何,支付将在支付期结束时完成。会不会像......

SELECT
      PreQry.*,
      PreQry.TotalHours,
      CASE when PreQry.TotalHours <= 15 
         then PreQry.TotalHours
         ELSE 15 end as OTAt50Pcnt,
      CASE when PreQry.TotalHours <= 15 
         then 0
         ELSE PreQry.TotalHours - 15 end as OTAt100Pcnt
   from
      ( select 
              tc.Employee_ID,
              SUM( tc.overtime_day + tc.overtime_night ) as totalHours,
              SUM( tc.overtime_day ) as totalOTDay,
              SUM( tc.overtime_night ) as totalOTNight
           from
              TimeCards tc
           group by
              tc.Employee_ID ) PreQry

这会产生类似

的内容
Employee_Id  TotalHours   Total Day O/T   Total Night O/T   O/T at 50%   O/T at 100% 
48           32           22 hours        10 hours          15 hrs       17 hrs

我认为这是一个更可行的解决方案,但仅提供作为Oracle日常汇总的替代方案。我确信具有更深入经验的人可能会提供更准确的服务。

答案 1 :(得分:1)

使用与原始格式相同的数据,即添加新列而不是新行,我们可以获得

With D AS (
  Select Employee_Id, "Date", Overtime_Day, Overtime_Night
       , SUM(Overtime_Day + Overtime_Night) 
         OVER (ORDER BY "Date") - Overtime_Night Total_Day
       , SUM(Overtime_Day + Overtime_Night) 
         OVER (ORDER BY "Date") Total_Night
  From   Data
)
SELECT Employee_Id
     , SUM(Case When Total_Day < 15 Then Overtime_Day
                When Total_Day < 15 + Overtime_Day 
                  Then Overtime_Day - (Total_Day - 15)
                Else 0 
           END) Overtime_Day_50
     , SUM(Case When Total_Night < 15 Then Overtime_Night 
                When Total_Night < 15 + Overtime_Night 
                  Then Overtime_Night - (Total_Night - 15)
                Else 0 
           END) Overtime_Night_50
     , SUM(Case When Total_Day < 15 Then 0
            When Total_Day < 15 + Overtime_Day Then Total_Day - 15
            When Total_Day > 15 Then Overtime_Day
       END) Overtime_Day_100
     , SUM(Case When Total_Night < 15 Then 0
            When Total_Night < 15 + Overtime_Night Then Total_Night - 15
            When Total_Night > 15 Then Overtime_Night
       END) Overtime_Night_100
FROM   D
GROUP BY Employee_Id

SQLFiddle demo

在CTE中,计算当天和晚上加班的滚动总数,在当天的总加班时间公式中减去Overtime_Night,得到上一次加班和{{当前行的1}} 在主查询中,Overtime_Day使用“简单”算法来计算值:

  • 如果总小时数小于15,则获取当前小时
  • 如果总小时数超过15但在当前小时的缓冲区内获得有效的当前小时部分
  • 否则不是50%

100%的部分正好相反

  • 如果总小时数小于15,我们不是100%
  • 如果总小时数超过15,但在当前小时的缓冲区内,则获得超过15
  • 的部分
  • 否则全部为100%

要有一个更通用的案例,如在OP的评论中,可以创建其他组,如

CASE

使用 , SUM(Case When Total_Day < %bStart% Then 0 When Total_Day < %bStart% + Overtime_Day Then Total_Day - %bStart% When Total_Day < %bEnd% And Total_Day > %bStart% Then Overtime_Day When Total_Day < %bEnd% + Overtime_Day Then Overtime_Day - (Total_Day - %bEnd%) Else 0 END) Overtime_Day_60 , SUM(Case When Total_Night < %bStart% Then 0 When Total_Night < %bStart% + Overtime_Night Then Total_Night - %bStart% When Total_Night < %bEnd% And Total_Night > %bStart% Then Overtime_Night When Total_Night < %bEnd% + Overtime_Night Then Overtime_Night - (Total_Night - %bStart%) Else 0 END) Overtime_Night_60 %bStart%作为块的起始值和结束值,但它不会非常灵活,使用两个以上的块,不同的方法更好,并且它还允许从查询中删除幻数以将它们移动到表中

%bEnd%

SQLFiddle demo ,数据已更改为白天和晚上都会更改块。

所有条件都是检查边界的不平等。

输出格式为

With D AS (
  SELECT Employee_Id, "Date", Overtime_Day, Overtime_Night
       , SUM(Overtime_Day + Overtime_Night) OVER (ORDER BY "Date") 
       - (Overtime_Day + Overtime_Night) Total_Begin
       , SUM(Overtime_Day + Overtime_Night) OVER (ORDER BY "Date")
       - Overtime_Night Total_Day
       , SUM(Overtime_Day + Overtime_Night) OVER (ORDER BY "Date") Total_Night
  FROM   Data
), Block AS (
  SELECT 1 ID, 'Overtime_50' Name, 0 bStart, 15 bEnd FROM DUAL
  UNION ALL
  SELECT 2, 'Overtime_60', 15, 20 FROM DUAL
  UNION ALL 
  SELECT 3, 'Overtime_100', 20, 999 FROM DUAL
)
  SELECT Employee_Id
       , block.Name
       , SUM(CASE WHEN Total_Begin < bStart AND Total_Day > bStart THEN Total_Day - bStart
                  WHEN Total_Day < bStart THEN 0
                  WHEN Total_Day <= bEnd THEN OverTime_Day
                  WHEN Total_Begin < bEnd AND Total_Day > bEnd THEN bEnd - Total_Begin
             END) Overtime_Day
       , SUM(CASE WHEN Total_Day >= bStart AND Total_Night < bEnd THEN Overtime_Night
                  WHEN Total_Begin < bEnd AND Total_Day > bEnd THEN 0
                  WHEN Total_Day > bStart AND Total_Night > bEnd THEN bEnd - Total_Day
                  WHEN Total_Day < bStart AND Total_Night > bStart THEN Total_Night - bStart
             END) Overtime_Night
  FROM   D
         INNER JOIN Block ON
               (Total_Begin < bStart AND Total_Night > bStart)
            OR (Total_Begin >= bStart AND Total_Begin < bEnd)
GROUP BY Employee_Id, block.Name, block.ID
ORDER BY block.ID

与原始OP输出请求略有不同,但如果有必要,从这里开始并不困难。

如果你想在一排&amp;多列格式,只需使用Employee_Id | Name | Overtime_Day | Overtime_Night 48 | Overtime_50 | 11 | 4 48 | Overtime_60 | 2 | 3 48 | Overtime_100 | 9 | 5 运算符。

PIVOT

输出格式为

    With D AS (
  SELECT Employee_Id, "Date", Overtime_Day, Overtime_Night
       , SUM(Overtime_Day + Overtime_Night) OVER (ORDER BY "Date") 
       - (Overtime_Day + Overtime_Night) Total_Begin
       , SUM(Overtime_Day + Overtime_Night) OVER (ORDER BY "Date")
       - Overtime_Night Total_Day
       , SUM(Overtime_Day + Overtime_Night) OVER (ORDER BY "Date") Total_Night
  FROM   Data
), Block AS (
  SELECT 1 ID, 'Overtime_50' Name, 0 bStart, 15 bEnd FROM DUAL
  UNION ALL
  SELECT 2, 'Overtime_60', 15, 20 FROM DUAL
  UNION ALL 
  SELECT 3, 'Overtime_100', 20, 999 FROM DUAL
)
SELECT * FROM (
    SELECT Employee_Id
       , block.Name
       , SUM(CASE WHEN Total_Begin < bStart AND Total_Day > bStart THEN Total_Day - bStart
                  WHEN Total_Day < bStart THEN 0
                  WHEN Total_Day <= bEnd THEN OverTime_Day
                  WHEN Total_Begin < bEnd AND Total_Day > bEnd THEN bEnd - Total_Begin
             END) Overtime_Day
       , SUM(CASE WHEN Total_Day >= bStart AND Total_Night < bEnd THEN Overtime_Night
                  WHEN Total_Begin < bEnd AND Total_Day > bEnd THEN 0
                  WHEN Total_Day > bStart AND Total_Night > bEnd THEN bEnd - Total_Day
                  WHEN Total_Day < bStart AND Total_Night > bStart THEN Total_Night - bStart
             END) Overtime_Night
    FROM   D
         INNER JOIN Block ON
               (Total_Begin < bStart AND Total_Night > bStart)
            OR (Total_Begin >= bStart AND Total_Begin < bEnd)
    GROUP BY Employee_Id, block.Name, block.ID
    ORDER BY block.ID
)PIVOT (SUM(OVERTIME_DAY) AS Day, SUM(OVERTIME_NIGHT) AS Night FOR (Name) in ('Overtime_50' as Overtime_50,'Overtime_60' as Overtime_60,'Overtime_100' as Overtime_100))