简体   繁体   English

SQL 从另一个表的余额中减去一个表中的值超过多行

[英]SQL Deduct value in one table from balance in another over multiple rows

I have a job that runs once per day against two tables, one containing an employees current holiday and PTO balances (dbo.Balance) and another containing new absences (dbo.Absence).我有一份每天针对两张表运行一次的工作,一张包含员工当前的假期和 PTO 余额 (dbo.Balance),另一张包含新的缺勤 (dbo.Absence)。 All values are in minutes.所有值都以分钟为单位。

I'm trying to use logic to determine how much time each absence (previous day) should be applied to holiday, pto, or unpaid.我正在尝试使用逻辑来确定每次缺勤(前一天)应该应用于假期、取力器或无薪的时间。 It should apply in the following order: 1) Holiday, 2) PTO, 3) Unpaid.它应该按以下顺序申请:1)假期,2)PTO,3)未付。 I was fine with the below code until an employee took two separate absences on the same day.我对下面的代码很好,直到一名员工在同一天两次单独缺勤。 I've been beating my head on this for a week and can't figure out how to get it to basically have a running balance for each bucket at run time.我已经为此苦苦挣扎了一周,无法弄清楚如何让它在运行时基本上对每个存储桶保持运行平衡。

The actual job is more complex and involves modifying start and end times for each record based on this output - so I do need a separate line for each absence record.实际的工作更复杂,涉及根据此 output 修改每条记录的开始和结束时间 - 所以我确实需要为每条缺勤记录单独一行。

dbo.Balance dbo.余额

| EmpId | HolidayBal | PTOBal |
|:------|:----------:|-------:|
|   1   |     60     |  120   |
|   2   |    240     |  600   |
|   3   |      0     |    0   |

dbo.Absence dbo.缺席

| EmpId | Time Taken | 
|:------|-----------:|
|   1   |    210     |
|   1   |     45     |
|   2   |    120     |
|   3   |    120     |

What I would like the result to look like:我希望结果看起来像什么:

| EmpId | TimeTaken  | HolidayUsed | PTOUsed | Unpaid |
|:------|:----------:|:-----------:|:-------:|:------:|
|   1   |    210     |      60     |   120   |   30   |
|   1   |     45     |       0     |     0   |   45   |
|   2   |    120     |     120     |     0   |    0   |
|   3   |    120     |       0     |     0   |  120   |

This is the code I have so far, just doesn't apply correctly when there is more than 1 absence in a day.这是我到目前为止的代码,只是在一天中有超过 1 次缺勤时无法正确应用。

SELECT 
        BAL.EmpId
        ,BAL.HolidayBalance
        ,BAL.PTOBalance
        ,AB.TimeTaken

        ,CASE --HOLIDAY CALCULATION
            WHEN BAL.HolidayBalance - AB.TimeTaken >= 0 THEN -- HOLIDAY BALANCE EXCEEDS TIME USED, ALL TIME USED AS HOLIDAY
                AB.TimeTaken
            WHEN BAL.HolidayBalance > 0 AND BAL.HolidayBalance - AB.TimeTaken < 0 THEN -- TIME USED EXCEEDS HOLIDAY BALANCE, ALL HOLIDAY BALANCE USED
                BAL.HolidayBalance
            ELSE 
                0
            END AS HolidayUsed
        ,CASE -- PTO CALCULATION
            WHEN BAL.HolidayBalance - AB.TimeTaken >= 0 THEN -- ALL TIME USED AS HOLIDAY
                0
            WHEN BAL.HolidayBalance > 0 AND BAL.HolidayBalance - AB.TimeTaken < 0 THEN -- HOLIDAY BALANCE WAS LESS THAN TIME USED, HAVE A REMAINDER
                CASE
                    WHEN ((BAL.PTOBalance) - (AB.TimeTaken - BAL.HolidayBalance)) >= 0 THEN -- HAVE ENOUGH PTO BALANCE TO COVER REMAINDER
                        ((BAL.PTOBalance) - (AB.TimeTaken - BAL.HolidayBalance))
                    WHEN  BAL.PTOBalance > 0 AND ((BAL.PTOBalance) - (AB.TimeTaken - BAL.HolidayBalance)) < 0 THEN -- HAVE A PTO BALANCE BUT DOES NOT COVER REMAINDER
                        BAL.PTOBalance
                    ELSE
                        0
                END
            WHEN BAL.PTOBalance > 0 AND BAL.HolidayBalance = 0 AND BAL.PTOBalance - AB.TimeTaken >=0 THEN -- HAVE PTO TO COVER IT AND NO HOLIDAY BALLANCE.
                AB.TimeTaken
            WHEN BAL.PTOBalance > 0 AND BAL.HolidayBalance = 0 AND AB.TimeTaken > BAL.PTOBalance THEN-- HAVE NO HOLIDAY AND PTO BALANCE DOES NOT COVER EVERYTHING
                BAL.PTOBalance
            ELSE 0
            END AS PTOUsed
            ,CASE -- UNPAID CALCULATION
                WHEN BAL.PTOBalance + BAL.HolidayBalance - AB.TimeTaken < 0 THEN --NOT ENOUGH PTO OR HOLIDAY TO COVER ABSENCE
                    AB.TimeTaken - (BAL.PTOBalance + BAL.HolidayBalance)
                ELSE    
                    0
            END AS Unpaid
FROM    dbo.Balance BAL
        INNER JOIN dbo.Absence AB ON BAL.EmpId = AB.EmpId

Thanks for y'alls input.谢谢大家的投入。 I finally figured it out.我终于弄明白了。 Had to basically add a running sum of the absence value (AbsenceSum) and then some tricky calculations to get everything to balance correctly.基本上必须添加缺席值(AbsenceSum)的运行总和,然后进行一些棘手的计算以使所有内容正确平衡。 Wanted to post my solution should someone else have something similar!如果其他人有类似的东西,想发布我的解决方案!

SELECT
    EmpId
    ,TimeTaken
    ,StartTime
    ,HolidayUsed
    ,PTOUsed
    ,CASE
        WHEN TimeTaken - (HolidayUsed + PTOUsed) > 0 THEN -- HAVE A BALANCE NEEDING UNPAID
            TimeTaken - (HolidayUsed + PTOUsed)
        ELSE
            0
    END AS Unpaid
FROM (
    SELECT 
        AB.EmpId
        ,TimeTaken
        ,AB.AbsenceSum
        ,StartTime
        ,CASE 
            WHEN BAL.HolidayBal > (AbsenceSum - TimeTaken) THEN -- HAVE HOL BALANCE AVAILABLE TO USE
                CASE 
                    WHEN TimeTaken >= BAL.HolidayBal - (AbsenceSum - TimeTaken) THEN -- NOT ENOUGH HOL TO COVER ENTIRE ABSENCE?
                        BAL.HolidayBal - (AbsenceSum - TimeTaken) -- NOT ENOUGH, USE WHAT'S LEFT
                    ELSE 
                        TimeTaken 
                    END -- ELSE USE ENTIRE ABSENCE AS HOL
            ELSE 
                0 
            END HolidayUsed -- NO AVAILABLE HOL
        ,CASE WHEN BAL.HolidayBal > (AbsenceSum - TimeTaken) THEN -- HAVE HOL BALANCE AVAILABLE TO USE
                (CASE 
                    WHEN TimeTaken >= BAL.HolidayBal - (AbsenceSum - TimeTaken) THEN -- HOL COVERED SOME BUT NOT ALL
                        CASE
                            WHEN BAL.PTOBal > 0 THEN --HAVE PTO AVAILABLE
                                CASE
                                    WHEN BAL.PTOBal >= TimeTaken - (BAL.HolidayBal - (AbsenceSum - TimeTaken)) THEN -- PTO COVERS ALL OF REMAINDER
                                        TimeTaken - (BAL.HolidayBal - (AbsenceSum - TimeTaken))
                                    ELSE --PTO ONLY COVERS SOME
                                        BAL.PTOBal
                                END
                            ELSE
                                0
                        END
                    ELSE --HOL COVERED ENTIRE ABSENCE
                        0
                END)                
            ELSE -- NO HOL TO USE, MOVE TO PTO
                CASE 
                    WHEN BAL.PTOBal - ((AbsenceSum - TimeTaken) - BAL.HolidayBal) > 0 THEN -- HAVE PTO TO USE?
                        CASE 
                            WHEN TimeTaken >= BAL.PTOBal - ((AbsenceSum - TimeTaken) - BAL.HolidayBal) THEN -- HAVE ENOUGH PTO TO COVER ENTIRE ABSENCE?
                                BAL.PTOBal - ((AbsenceSum - TimeTaken) - BAL.HolidayBal) -- NOT ENOUGH, USE WHAT IS LEFT
                            ELSE 
                                TimeTaken 
                        END
                    ELSE
                        0
                END
            END AS PTOUsed
    FROM (
        SELECT EmpId, TimeTaken, ScheduleID, StartTime,
        SUM(TimeTaken) OVER (PARTITION BY EmpId ORDER BY ScheduleID, StartTime) AbsenceSum
        FROM dbo.Absence
    )AB 
        INNER JOIN dbo.Balance BAL ON BAL.EmpId = AB.EmpId
    )A
ORDER BY EmpId

This won't give you separate lines for each case, but it will do the calculation correctly.这不会为每种情况提供单独的行,但它会正确进行计算。 It might be a great interim solution.这可能是一个很好的临时解决方案。

Create a CTE with the sum of all the time off for the day as follows:创建一个包含当天所有休息时间总和的 CTE,如下所示:

WITH total_absense AS 
(
    SELECT DISTINCT EmpID, SUM([Time Taken]) OVER(PARTITION BY EmpID)
    FROM dbo.Absense
)

Then replace dbo.absense with total_absense in your script.然后在脚本中将 dbo.absense 替换为 total_absense。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM