【问题标题】:Calculate a value based on previous value (loop)根据先前的值计算一个值(循环)
【发布时间】:2017-01-11 18:16:22
【问题描述】:

有人可以帮我了解使用什么类型的功能或方向来解决以下问题吗?我已经完成了脚本的大部分工作,但我一直在试图弄清楚如何从上到下一次定义一个日期。例如,计算第一行的第一个日期,然后根据第一行的第一个日期计算第二行的第二个日期。

我创建的示例表:

Employee  | WorkDate  | #ofBreaksInWks  |  EndDate   |  StartDate
123         1/4/2016                                    1/4/2016
123         1/5/2016            
123         1/6/2016      2 
123         1/27/2016   
123         1/28/2016     5               *1/28/2016   *3/10/2016
123         3/10/2016   
123         3/9/2016      1 
123         3/25/2016       
123         4/1/2016            
123         4/10/2016           
123         4/15/2016     4           

=Today's date *05/15/2016

=Values with asterisk* will be calculative

如果我工作了 4 周或

  1. 我已经定义了所有工作日期MIN 的第一个开始日期"1/4/2016"
  2. 我查找大于 3 的第一个休息时间。我看到有 5 周的休息时间。我必须检查我的工作量是否超过了起飞时间。 WorkDate 减去 StartDate (1/28/2016 -3/10/2016)。如果超过 5 周,我会指定一个结束日期,即 WorkDate
  3. 然后我去找下一个。我看到 4 周 (4/15/2016 - *05/15/2016) ,所以我计算一下员工是否工作超过 4 周。在这种情况下,员工已经工作了 5 周。由之前的StartDate3/10/2016 减去4/15/2016 (WorkDate) 计算得出。

我知道如何计算开始日期和结束日期,但我不确定如何一次完成这个,因为下一行都取决于前一个值。请帮忙!

【问题讨论】:

  • 您的日期字段的数据类型是什么?
  • @Shell 日期时间
  • 我不知道数据库是否是这个问题的最佳解决方案。 SQL Server 不是为时间序列分析而设计的。理想情况下,每一行都应该是一个数据单元,完全独立于该表中的其他单元。也许 Excel 是一个更好的工具,或者旨在处理这些系列依赖关系的工具。
  • 您应该查看 LEAD/LAG,而不是循环。这也有一种孤岛和差距情况的声音(你应该用谷歌搜索,你会找到一些很好的例子)。无论哪种方式,循环都不是要走的路。
  • 所以“休息”就像一个完整的周日到周六一周,没有记录工作日??

标签: sql sql-server loops date sql-server-2012


【解决方案1】:

您可以通过使用 Temp Table 和具有 Processed 标志的 While 循环来实现您想要的。

            IF (OBJECT_ID('tempdb..#YourTempTable') IS NOT NULL)
            BEGIN
                DROP TABLE #YourTempTable;
            END

            -- Populate Temp Table
            SELECT
                Processed = 0,
                Id,
                Employee,
                WorkDate,
                EndDate,
                StartDate
            INTO
                #YourTempTable
            FROM
                DataSourceTable;

                DECLARE @Id int = 0;
                DECLARE @LastId int = 0;
                DECLARE @Employee nvarchar(30) = '';
                DECLARE @WorkDate int = 0;
                DECLARE @LastWorkDate int = 0;

                WHILE EXISTS (SELECT TOP 1 Processed FROM #YourTempTable WHERE Processed = 0)
                BEGIN
                    SELECT TOP 1
                        @Id = Id,
                        @Employee = Employee,
                        @WorkDate = WorkDate
                    FROM
                        #YourTempTable
                    WHERE
                        Processed = 0;

                    UPDATE
                        DataSourceTable
                    SET
                        NewDefinedDate = @WorkDate -- Combined with something with @LastWorkDate
                    WHERE 
                        Id = @Id;

                    SET @LastId = @Id;
                    SET @LastWorkDate = @LastWorkDate;

                    UPDATE
                        #YourTempTable
                    SET
                        Processed = 1
                    WHERE
                        Id = @Id;
                END
            END

            IF (OBJECT_ID('tempdb..#YourTempTable') IS NOT NULL)
            BEGIN
                DROP TABLE #YourTempTable;
            END

【讨论】:

  • 这是个好主意。我将做一些关于while循环的研究,并会尝试你的建议。会告诉你这件事的进展的。谢谢!
  • 从性能角度来看,在此处循环并不是一个好方法。您也在使用 top 1 但该查询没有顺序,这意味着您不知道将返回哪一行。
【解决方案2】:
;with weeks as (    
     select
         Employee,
         datediff(week, '20000102', WorkDate) as weekNum,
         min(WorkDate) as workDate,
         row_number() over (
             partition by Employee
             order by datediff(week, '20000102', WorkDate)
         ) as rowNum
     from emp
     group by Employee, datediff(week, '20000102', WorkDate)
), islands as (
    select
        Employee,
        min(workDate) as workDate,
        datediff(week, min(workDate), max(workDate)) + 1 as workWeeks
    from weeks
    group by Employee, weekNum - rowNum
), gaps as (
    select *,
        datediff(week,
            workDate,
            lead(workDate) over (partition by Employee order by workDate)
        ) - workWeeks as breakWeeks
    from islands
)
select * from gaps
-- where breakWeeks > workWeeks; ??

我仍然不明白这个问题的全部性质,但这是我几天前在手机上输入的先前尝试的工作版本。它确定连续工作周的周数,然后计算其自身与下一次运行开始之间的非工作周数。这是解决问题的非循环方法。希望它能让您更接近解决方案。

http://rextester.com/LFAH31486

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多