使用带有记录VALUE UPDATE的DATEDIFF的递归SUM

时间:2013-08-25 04:06:31

标签: sql sql-update partitioning datediff

我正致力于实施用于监控绩效的积分跟踪系统。员工的目标是根据需要提供少量积分。有一个积分系统,允许员工通过在不同时间段内表现更好来减少积分。在30天的表现中,积分最多减少1分,如果没有发生事故则减少60分,如果没有事故则减少到90分。它们不能累积负值,并且点从最旧的日期到最新的日期滚动。下面的示例表是已导入SQL Server 2012数据库的现有表的表示形式。

Employee    Points    Date  Previous    Note
Smith, Joe  0.25    3/21/2013       
Smith, Joe  1       4/1/2013        
Smith, Joe  0.25    5/6/2013        
Smith, Joe  0.5     5/8/2013        
Smith, Joe  1       7/10/2013       
Jones, Tom  1       4/10/2013       
Jones, Tom  1       4/18/2013       
Jones, Tom  0.5     4/22/2013       
Jones, Tom  2       6/25/2013       
Jones, Tom  0.25    7/26/2013       
Jones, Tom  0.25    7/28/2013       

由于动态使用数据,转到多个来源(如C#,Excel,Email),因此需要构建确定性函数并生成更新的表或视图,以显示类似于以下内容的内容。下面的例子将是报告在2013年5月18日提取的报告。每个员工都会得到1分。因为Joe的第一个点是.25,它将被设置为0,下一个点1.00将被剩余的0.75减少到0.25的值。因为汤姆有一个满分作为他的第一个点,它将从1分中减去,导致该日期为0分。在更新Point值之前,该函数还应标记Points Column的先前值。该函数应该提供关于更改点的原因的注释,注释字段中的日期会很好,但是NoteDate的单独Column可以包含日期。该函数可以在INSERT / UPDATE触发器上运行,因为每日使用率非常低。它应该检查当前日期与最后一个点日期。

Employee    Points  Date    Previous    Note
Smith, Joe  0.00    3/21/2013   0.25    Rolled Off by System - 30 Day Policy 05/01/2013
Smith, Joe  0.25    4/1/2013    1.00    Rolled Off by System - 30 Day Policy 05/01/2013
Smith, Joe  0.25    5/6/2013        
Smith, Joe  0.50    5/8/2013        
Jones, Tom  0.00    4/10/2013   1.00    Rolled Off by System - 30 Day Policy 05/22/2013
Jones, Tom  1.00    4/18/2013       
Jones, Tom  0.50    4/22/2013       

截至今天的2013年8月24日,结果应反映以下内容。乔总共得到0分,而汤姆总共得到2分,到第28分只得到1分。大约有2,000条记录,因此很难手动更改这些记录。根据该系统的性质及其实现,可以运行1个脚本来协调当前记录,并有一个单独的脚本来管理正在进行的条目。

Employee    Points  Date    Previous    Note
Smith, Joe  0.00    3/21/2013   0.25    Rolled Off by System - 30 Day Policy 05/01/2013
Smith, Joe  0.00    4/1/2013    0.25    Rolled Off by System - 30 Day Policy 06/07/2013
Smith, Joe  0.00    5/6/2013    0.25    Rolled Off by System - 30 Day Policy 06/07/2013
Smith, Joe  0.00    5/8/2013    0.50    Rolled Off by System - 30 Day Policy 06/07/2013
Smith, Joe  0.00    7/10/2013   1.00    Rolled Off by System - 30 Day Policy 08/10/2013
Jones, Tom  0.00    4/10/2013   1.00    Rolled Off by System - 30 Day Policy 05/22/2013
Jones, Tom  0.00    4/18/2013   1.00    Rolled Off by System - 60 Day Policy 06/21/2013
Jones, Tom  0.00    4/22/2013   0.50    Rolled Off by System - 30 Day Policy 07/25/2013
Jones, Tom  1.50    6/25/2013   2.00    Rolled Off by System - 30 Day Policy 07/25/2013
Jones, Tom  0.25    7/26/2013       
Jones, Tom  0.25    7/28/2013       

我开始在EmployeePerformance TABLE上使用以下内容,以创建一个视图,以显示每个员工的每个记录与其ID之间的天数。然后我使用DENSE_RANK()为每位员工编号,并提供出勤视图中的所有信息。我最初的想法是使用递增的VARIABLE为每个员工循环一个WHILE循环,然后检查DATE_DIFFERENCE以查看是否有任何大于30的值。如果不是,我计划将该员工的所有记录插入到最终表中,并从Attend2表中删除它们。比我想象的更多的脚本是必要的。我反而想过使用CURSOR来创建一次性传递,然后再调用另一个函数。我对CURSOR的经验不多。

CREATE VIEW Attendance AS
SELECT A.ID, A.FullName, A.EmployeeID, A.AttendanceDate, A.OccurrenceAmount, A.Comments, A.RecordCreatedDate, A.RecordCreatedUser, (DATEDIFF(DAY, A.AttendanceDate, B.AttendanceDate))*-1 AS DATE_DIFFERENCE
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY EmployeeID ORDER BY AttendanceDate) AS Row_Num, * 
FROM dbo.EmployeePerformance) AS A
LEFT JOIN (
SELECT ROW_NUMBER() OVER (PARTITION BY EmployeeID ORDER BY AttendanceDate) AS Row_Num, * 
FROM dbo.EmployeePerformance) AS B
ON A.EmployeeID=B.EmployeeID AND A.Row_Num=B.Row_Num+1  


SELECT *, DENSE_RANK() OVER (ORDER BY Attendance.EmployeeID) AS Row_Num
INTO dbo.Attend2
FROM dbo.Attendance

我有一些想法可以解决这个问题,但我希望有人可能遇到过类似情况,或者曾经遇到类似这样的问题。我宁愿把它构建成一个我可以触发的简洁代码片段,并且在低容量环境中使用SQL数据库我不是非常担心性能,但是我想找到一个能够保持性能的良好解决方案心神。感谢您提供的任何帮助或反馈。

1 个答案:

答案 0 :(得分:1)

我不确定我是否完全遵循您的方法/期望的结果,但对于初学者,您可以使用LEAD代替JOIN / ROW_NUMBER()来获取DATEDIFF

SELECT ID
     , FullName
     , EmployeeID
     , AttendanceDate
     , OccurrenceAmount
     , Comments
     , RecordCreatedDate
     , RecordCreatedUser
     , DATEDIFF(DAY, AttendanceDate, LEAD(AttendanceDate) OVER(PARTITION BY EmployeeID ORDER BY AttendanceDate))*-1 AS DATE_DIFFERENCE
FROM dbo.EmployeePerformance)