将WHILE转换为CROSS APPLY

时间:2019-06-26 21:20:10

标签: sql sql-server tsql cross-apply

我有一个标量函数,正在转换为TVF。

在标量函数中,我具有以下while语句:

WHILE @ReviewDueDate < getdate() 
    SET @ReviewDueDate = DATEADD(DAY, 30, @ReviewDueDate)

将其放在TVF中时,我使用CROSS APPLY(在转换方面获得了一些帮助),并将其转换为以下代码:

CROSS APPLY (
    VALUES(DATEADD(DAY, 30 * CEILING(( IIF(CAST(GETDATE() AS TIME) > CAST(CA1.ReviewDueDate AS TIME), 1, 0) + DATEDIFF(DAY, CA1.ReviewDueDate, GETDATE()) ) / 30.0), CA1.ReviewDueDate))
) CA2(ReviewDueDate)

我不太清楚转换部分。
我了解到,在@ReviewDate < getdate()时,需要将30天添加到@ReviewDate中。
当我尝试理解转换后的CROSS APPLY代码时,我有点迷失。

现在对我来说最困难的部分是:
我还有两个while循环,如下所示:

WHILE @ReviewDueDate < getdate() 
    SET @ReviewDueDate = DATEADD(Month, 6, @ReviewDueDate)
WHILE @ReviewDueDate < getdate() 
    SET @ReviewDueDate = DATEADD(YEAR, 1, @ReviewDueDate)

所以我必须将其转换为在CROSS APPLY中使用。

我不清楚find while循环是如何转换的,因此我在与另外两个进行斗争。
感谢您对首次转换的任何帮助。 如果有人可以帮助转换其他两个,那也将有所帮助。

1 个答案:

答案 0 :(得分:1)

您本身不需要cross apply。显然,它只是用来从多个位置引用计算出的表达式。

cross apply中的公式直接计算需要添加到@ReviewDueDate的30天块中的多少,以便超过getdate()

  • 采用@ReviewDueDate与今天之间的天差。
  • 如果@ReviewDueDate的一天中的时间大于今天的一天中的时间,请添加一天。
  • 将得出的天数除以30.0,以确保它不是integer division
  • 把它弄圆。那就是您需要添加的30天块的数量。
  • 将其乘以30得到天数。

这不能很好地转换为非天间隔,例如几个月或几年。例如,这:

DATEADD(month, 6 * CEILING(( IIF(CAST(getdate() AS TIME) > CAST(CA1.ReviewDueDate AS TIME), 1, 0) + DATEDIFF(month, CA1.ReviewDueDate, getdate()) ) / 6.0), CA1.ReviewDueDate);

将会是错误的,因为如果今天的日期在同一个月并且CA1.ReviewDueDate的日期时间不大于今天的时间,它将返回未修改的CA1.ReviewDueDate

因此,您需要调整除月/年之外的天数差异:

DATEADD(
  month,
  6 * CEILING(  (IIF(  (day(getdate()) > day(CA1.ReviewDueDate) or CAST(getdate() AS TIME) > CAST(CA1.ReviewDueDate AS TIME)), 1, 0) + DATEDIFF(month, CA1.ReviewDueDate, getdate())) / 6.0  ),
  CA1.ReviewDueDate
)
DATEADD(
  year,
  1 * CEILING(  (IIF(  (month(getdate()) > month(CA1.ReviewDueDate) or day(getdate()) > day(CA1.ReviewDueDate) or CAST(getdate() AS TIME) > CAST(CA1.ReviewDueDate AS TIME)), 1, 0) + DATEDIFF(year, CA1.ReviewDueDate, getdate())) / 1.0  ),
  CA1.ReviewDueDate
)

所有严格假设CA1.ReviewDueDate总是小于getdate()的情况。如果不是,那么您希望将整个内容包装到另一个case when ... end中来处理这种情况,并在今天已经过去时执行其他操作。