SQL 从多行的另一张表的余额中扣除一个表中的值
Posted
技术标签:
【中文标题】SQL 从多行的另一张表的余额中扣除一个表中的值【英文标题】:SQL Deduct value in one table from balance in another over multiple rows 【发布时间】:2021-01-11 21:04:09 【问题描述】:我的工作每天针对两张表运行一次,一张包含员工当前的假期和 PTO 余额 (dbo.Balance),另一张包含新的缺勤 (dbo.Absence)。所有值都以分钟为单位。
我正在尝试使用逻辑来确定每次缺勤(前一天)应该将多少时间应用于假期、pto 或无薪。它应该按以下顺序申请:1)假期,2)PTO,3)未付。我对下面的代码很好,直到一名员工在同一天两次单独缺勤。我已经为此苦苦挣扎了一个星期,无法弄清楚如何让它在运行时基本上为每个存储桶保持运行平衡。
实际的工作更复杂,涉及根据此输出修改每条记录的开始和结束时间 - 所以我确实需要为每条缺勤记录单独一行。
dbo.Balance
| EmpId | HolidayBal | PTOBal |
|:------|:----------:|-------:|
| 1 | 60 | 120 |
| 2 | 240 | 600 |
| 3 | 0 | 0 |
dbo.Absence
| EmpId | Time Taken |
|:------|-----------:|
| 1 | 210 |
| 1 | 45 |
| 2 | 120 |
| 3 | 120 |
我希望结果是什么样的:
| 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 |
这是我到目前为止的代码,只是当一天缺勤超过 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
【问题讨论】:
【参考方案1】:感谢大家的意见。我终于弄明白了。基本上必须添加缺席值(AbsenceSum)的运行总和,然后进行一些棘手的计算以使所有内容正确平衡。如果其他人有类似的情况,想发布我的解决方案!
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
【讨论】:
【参考方案2】:这不会为每种情况提供单独的行,但它会正确地进行计算。这可能是一个很好的临时解决方案。
使用当天所有休息时间的总和创建一个 CTE,如下所示:
WITH total_absense AS
(
SELECT DISTINCT EmpID, SUM([Time Taken]) OVER(PARTITION BY EmpID)
FROM dbo.Absense
)
然后在您的脚本中将 dbo.absense 替换为 total_absense。
【讨论】:
感谢您的帮助!这项工作实际上有点复杂,我需要为每个缺勤记录单独读出以进行下游处理。我试图为这个问题简化它。SELECT EmpID, SUM([Time Taken]) FROM dbo.Absense GROUP BY EmpID
是正确的做法
听起来您可能需要另一列。也许只有一个 ROW_NUMBER 列和 EMP_ID。如果你有一个有效的 ORDER BY 算法,你也许可以在 ROW_NUMBER 大于 1 时添加一些额外的逻辑来考虑已经改变的余额。以上是关于SQL 从多行的另一张表的余额中扣除一个表中的值的主要内容,如果未能解决你的问题,请参考以下文章
在 MySQL 中,如何将一张表的内容复制到同一个数据库中的另一张表中?
在 MySQL 中,如何将一张表的内容复制到同一个数据库中的另一张表中?
在 MySQL 中,如何将一张表的内容复制到同一个数据库中的另一张表中?
两个表匹配,匹配上把一张表的值复制到另一张表的sql语句怎么写