使用工作日和日历日更新 table

Updating table with business day and calendar day

我在 SQL Server 2012 中有一个 table 每个月手动更新以反映文件预计进入的日期。日期规则已经有值但预期日期列是手动更新的。如果预计在 BD1(第 1 个工作日),我将更新到该月的第一个非周末日。如果它预计在 CD1(Calenday Day 1)上,我将更新到 1st,无论它是在工作日还是周末等等。是否可以编写一个更新查询来循环遍历值并自动更新?我无法确定更新到正确的工作日。

date rule  | March expected date | April expected date |
--------------------------------------------------------
| BD1      | 3/1/2017            | 4/3/2017            |
| BD2      | 3/2/2017            |                     |
| BD3      | 3/3/2017            |                     |
| BD4      | 3/6/2017            |                     |
| BD5      |                     |                     |
| BD6      |                     |                     |
| CD1      | 3/1/2017            |                     |
| CD2      | 3/2/2017            |                     |
| CD3      | 3/3/2017            |                     |
| CD4      | 3/4/2017            |                     |
| CD5      | 3/5/2017            |                     |
| CD6      | 3/6/2017            |                     |

我使用以下代码计算第一个工作日

SELECT DATEADD(DAY,
CASE
    (DATEPART(WEEKDAY, DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)) + @@DATEFIRST - 1) % 7
    WHEN 6 THEN 2 
    WHEN 7 THEN 1
    ELSE 0
END,
DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)

)

但是到了第 4 个工作日,它会给我 2017 年 3 月 4 日,也就是星期六,而不是 2017 年 3 月 6 日,也就是接下来的星期一。我对如何解决这个问题感到困惑。我认为循环更新查询最好

你可以使用这个逻辑..在哪里可以找到跳过周末的下一个工作日。

declare @datetoday date = '2017-04-01'


select iif(datepart(dw,@datetoday)=1, dateadd(day,1,@datetoday),iif(datepart(dw,@datetoday)=7,dateadd(day,2,@datetoday),@datetoday))

A calendar/tally table 可以解决问题,但我们可以使用临时计数 table.

Declare @Date1 date = '2017-03-01'
Declare @Date2 date = EOMonth(@Date1)

Select *
 From (
        Select Date    = D
              ,WeekDay = DateName(WEEKDAY,D)
              ,DayCode = concat('BD',Row_Number() over(Order By D))
         From (Select Top (DateDiff(DD,@Date1,@Date2)+1) D=DateAdd(DD,-1+Row_Number() Over (Order By Number),@Date1) From  master..spt_values) A
         Where DateName(WEEKDAY,D) Not in ('Saturday','Sunday')
        Union All
        Select Date    = D
              ,WeekDay = DateName(WEEKDAY,D)
              ,DayCode = concat('CD',Row_Number() over(Order By D))
         From (Select Top (DateDiff(DD,@Date1,@Date2)+1) D=DateAdd(DD,-1+Row_Number() Over (Order By Number),@Date1) From  master..spt_values) A
      ) A
 Where substring(DayCode,3,2)<=6

Returns

Date        WeekDay     DayCode
2017-03-01  Wednesday   BD1
2017-03-02  Thursday    BD2
2017-03-03  Friday      BD3
2017-03-06  Monday      BD4  --< Notice BD4 is Monday
2017-03-07  Tuesday     BD5
2017-03-08  Wednesday   BD6
2017-03-01  Wednesday   CD1
2017-03-02  Thursday    CD2
2017-03-03  Friday      CD3
2017-03-04  Saturday    CD4
2017-03-05  Sunday      CD5
2017-03-06  Monday      CD6

给你。此递归 CTE 将为您提供整个月的 BD:

declare @forwhichdate datetime
set @forwhichdate ='20170401'
;with bd as(
select 
DATEADD(DAY,
CASE
    (DATEPART(WEEKDAY, DATEADD(MONTH, DATEDIFF(MONTH, 0, @forwhichdate), 0)) + @@DATEFIRST - 1) % 7
    WHEN 6 THEN 2 
    WHEN 7 THEN 1
    ELSE 0
END,
DATEADD(MONTH, DATEDIFF(MONTH, 0, @forwhichdate), 0)
) as bd, 1 as n
UNION ALL
SELECT DATEADD(DAY,
CASE
    (DATEPART(WEEKDAY, bd.bd) + @@DATEFIRST - 1) % 7
    WHEN 5 THEN 3
    WHEN 6 THEN 2
    ELSE 1
END,
bd.bd
) as db, 
bd.n+1
from bd where month(bd.bd) = month(@forwhichdate)
)
select * from bd

结果:

bd                      n
----------------------- -----------
2017-04-03 00:00:00.000 1
2017-04-04 00:00:00.000 2
2017-04-05 00:00:00.000 3
2017-04-06 00:00:00.000 4
2017-04-07 00:00:00.000 5
2017-04-10 00:00:00.000 6
2017-04-11 00:00:00.000 7
2017-04-12 00:00:00.000 8
2017-04-13 00:00:00.000 9
2017-04-14 00:00:00.000 10
2017-04-17 00:00:00.000 11
2017-04-18 00:00:00.000 12
2017-04-19 00:00:00.000 13
2017-04-20 00:00:00.000 14
2017-04-21 00:00:00.000 15
2017-04-24 00:00:00.000 16
2017-04-25 00:00:00.000 17
2017-04-26 00:00:00.000 18
2017-04-27 00:00:00.000 19
2017-04-28 00:00:00.000 20
2017-05-01 00:00:00.000 21

(21 row(s) affected)

然而,实际上您的查询还应该检查节假日。

恕我直言,您的 table 结构在两个方面是错误的, i) daterule 列应该像我的 CTE.Then 计算那样分成 2 列会容易得多。

ii) 其次,你不会让你的专栏保持这样的旋转 manner.this 它会继续 increasing.So 希望你以正确的方式处理你的要求。

牢记您当前的结构,

declare @t table(daterule varchar(20),Marchexpected date , Aprilexpected date )
insert into @t VALUES
('BD1','3/1/2017', '4/3/2017')
,('BD2','3/2/2017',  null       )
,('BD3','3/3/2017',    null  )
,('BD4','3/4/2017',    null  )
,('BD5',  null      ,  null  )
,('BD6',  null      ,  null  )
,('CD1','3/1/2017',    null  )
,('CD2','3/2/2017',    null  )
,('CD3','3/3/2017',    null  )
,('CD4','3/4/2017',    null  )
,('CD5','3/5/2017',    null  )
,('CD6','3/6/2017',    null  )
declare @AprilDate date='2017-04-01'
;With CTE as
(
SELECT *
,case when daterule like 'BD%' then 'BD' else 'CD' end ruletype
,cast(replace(replace(daterule,'BD',  '' ),'CD','') as int) ruletypeid
 from @t
)
select daterule,Marchexpected
,case when datename(dw, dateadd(day,(ruletypeid-1),@AprilDate))='Saturday'
then dateadd(day,(ruletypeid+1),@AprilDate)
when datename(dw, dateadd(day,(ruletypeid-1),@AprilDate))='Sunday'
then dateadd(day,(ruletypeid),@AprilDate)
else
dateadd(day,(ruletypeid-1),@AprilDate)
END
Aprilexpected
from cte
where   ruletype='BD'

union ALL


select daterule,Marchexpected
,dateadd(day,(ruletypeid-1),@AprilDate)
from cte
where   ruletype='CD'