更新表与营业日和日历日

时间:2017-03-28 23:32:23

标签: sql sql-server sql-server-2012 sql-update sql-date-functions

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

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时,它会给我3/4/2017这是一个星期六,而不是3/6/2017,这是下周一。我很难知道如何解决这个问题。我认为循环更新查询是最好的

4 个答案:

答案 0 :(得分:1)

你去吧。这个递归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)

但是,实际上您的查询还应该检查假期。

答案 1 :(得分:0)

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

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))

答案 2 :(得分:0)

日历/计数表可以解决问题,但我们可以使用ad-hod计数表。

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

返回

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

答案 3 :(得分:0)

恕我直言,你的桌面结构有两种错误, i)daterule列应该像我的CTE一样分为两列。然后计算将更加容易。

ii)其次,你不能以专业的方式保持你的专栏。这将继续增加。所以希望你以正确的方式处理你的要求。

牢记当前的结构,

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'