如何递归计算 SQL 的年度展期?
How to recursively calculate yearly rollover in SQL?
我需要为一个跟踪人们何时休假的系统计算每年的结余。
翻转计算本身很简单:[TOTALDAYSALLOWED] - [USED]
前提是数字不高于[MAXROLLOVER] (and > 0)
变得复杂的是 [TOTALDAYSALLOWED]
列,该列 [NUMDAYSALLOWED]
与上一年的滚动相结合以获得当年可以使用的总天数。
我已经尝试了几种不同的方法来进行此计算,但所有这些方法都未能将上一年的结转计算为当年允许天数的一部分。
为使用的滞后天数创建列,将数据连接到自身但后移一年等。我没有包括我尝试过的代码示例,因为所有尝试的方法都是错误的。那只会让这么长的 post 变得更长。
这是我正在处理的数据:
这是计算后的样子:
这是按人计算的,所以这里不需要考虑任何个人 ID。 DAYTYPE 目前只有一个值,但我想将它包括在计算中以防添加另一个值。 [HOW] 列只是为了清楚起见 post。
下面是生成示例数据的一些代码(SQL 服务器或 Azure SQL):
IF OBJECT_ID('tempdb..#COUNTS') IS NOT NULL DROP TABLE #COUNTS
CREATE TABLE #COUNTS (USED INT, DAYTYPE VARCHAR(20), THEYEAR INT)
INSERT INTO #COUNTS (USED, DAYTYPE, THEYEAR)
SELECT 1, 'X', 2019
UNION
SELECT 3, 'X', 2020
UNION
SELECT 0, 'X', 2021
IF OBJECT_ID('tempdb..#ALLOWANCES') IS NOT NULL DROP TABLE #ALLOWANCES
CREATE TABLE #ALLOWANCES (THEYEAR INT, DAYTYPE VARCHAR(20), NUMDAYSALLOWED INT, MAXROLLOVER INT)
INSERT INTO #ALLOWANCES (THEYEAR, DAYTYPE, NUMDAYSALLOWED, MAXROLLOVER)
SELECT 2019, 'X', 3, 3
UNION
SELECT 2020, 'X', 3, 3
UNION
SELECT 2021, 'X', 3, 3
SELECT C.*, A.NUMDAYSALLOWED, A.MAXROLLOVER
FROM #COUNTS C
JOIN #ALLOWANCES A ON C.DAYTYPE = A.DAYTYPE AND C.THEYEAR = A.THEYEAR
棘手的部分是限制展期金额。使用 window 函数也许可以做到这一点,但我认为使用递归查询更容易做到这一点:
with
data as (
select c.*, a.numdaysallowed, a.maxrollover,
row_number() over(partition by c.daytype order by c.theyear) rn
from #counts c
inner join #allowances a on a.theyear = c.theyear and a.daytype = c.daytype
),
cte as (
select d.*,
numdaysallowed as totaldaysallowed,
numdaysallowed - used as actualrollover
from data d
where rn = 1
union all
select d.*,
d.numdaysallowed + c.actualrollover,
case when d.numdaysallowed + c.actualrollover - d.used > d.maxrollover
then 3
else d.numdaysallowed + c.actualrollover - d.used
end
from cte c
inner join data d on d.rn = c.rn + 1 and d.daytype = c.daytype
)
select * from cte order by theyear
我需要为一个跟踪人们何时休假的系统计算每年的结余。
翻转计算本身很简单:[TOTALDAYSALLOWED] - [USED]
前提是数字不高于[MAXROLLOVER] (and > 0)
变得复杂的是 [TOTALDAYSALLOWED]
列,该列 [NUMDAYSALLOWED]
与上一年的滚动相结合以获得当年可以使用的总天数。
我已经尝试了几种不同的方法来进行此计算,但所有这些方法都未能将上一年的结转计算为当年允许天数的一部分。
为使用的滞后天数创建列,将数据连接到自身但后移一年等。我没有包括我尝试过的代码示例,因为所有尝试的方法都是错误的。那只会让这么长的 post 变得更长。
这是我正在处理的数据:
这是计算后的样子:
这是按人计算的,所以这里不需要考虑任何个人 ID。 DAYTYPE 目前只有一个值,但我想将它包括在计算中以防添加另一个值。 [HOW] 列只是为了清楚起见 post。
下面是生成示例数据的一些代码(SQL 服务器或 Azure SQL):
IF OBJECT_ID('tempdb..#COUNTS') IS NOT NULL DROP TABLE #COUNTS
CREATE TABLE #COUNTS (USED INT, DAYTYPE VARCHAR(20), THEYEAR INT)
INSERT INTO #COUNTS (USED, DAYTYPE, THEYEAR)
SELECT 1, 'X', 2019
UNION
SELECT 3, 'X', 2020
UNION
SELECT 0, 'X', 2021
IF OBJECT_ID('tempdb..#ALLOWANCES') IS NOT NULL DROP TABLE #ALLOWANCES
CREATE TABLE #ALLOWANCES (THEYEAR INT, DAYTYPE VARCHAR(20), NUMDAYSALLOWED INT, MAXROLLOVER INT)
INSERT INTO #ALLOWANCES (THEYEAR, DAYTYPE, NUMDAYSALLOWED, MAXROLLOVER)
SELECT 2019, 'X', 3, 3
UNION
SELECT 2020, 'X', 3, 3
UNION
SELECT 2021, 'X', 3, 3
SELECT C.*, A.NUMDAYSALLOWED, A.MAXROLLOVER
FROM #COUNTS C
JOIN #ALLOWANCES A ON C.DAYTYPE = A.DAYTYPE AND C.THEYEAR = A.THEYEAR
棘手的部分是限制展期金额。使用 window 函数也许可以做到这一点,但我认为使用递归查询更容易做到这一点:
with
data as (
select c.*, a.numdaysallowed, a.maxrollover,
row_number() over(partition by c.daytype order by c.theyear) rn
from #counts c
inner join #allowances a on a.theyear = c.theyear and a.daytype = c.daytype
),
cte as (
select d.*,
numdaysallowed as totaldaysallowed,
numdaysallowed - used as actualrollover
from data d
where rn = 1
union all
select d.*,
d.numdaysallowed + c.actualrollover,
case when d.numdaysallowed + c.actualrollover - d.used > d.maxrollover
then 3
else d.numdaysallowed + c.actualrollover - d.used
end
from cte c
inner join data d on d.rn = c.rn + 1 and d.daytype = c.daytype
)
select * from cte order by theyear