我创建了一个“假日”表格,其中包含3列[Date]
日期格式,[BusinessDay]
,它们是假日或周末的Y / N和[NameofDay] varchar(50)
。周六,周日,圣诞节都标志着未来十年。
我现在需要做的是弄清楚如何通过不计算工作日来确定需要完成请求的日期。我已阅读,阅读和阅读,但没有看到任何有用的东西。在我的头脑中总是很简单。
我想要解决的问题:如果请求是“优先级”,则截止日期[DueDate]
将是请求日期后的10个工作日[TransactionDate]
,如果是“优先级”,则为5个工作日“危急”。
答案 0 :(得分:4)
由于@Andomar带来的问题的递归性质,我建议一个替代答案(它恰好也更简单,但需要窗口函数可用)。这样做的目的是从日历中加入有效工作日TransactionDate
,然后为每个请求id
根据需要找到第5行或第10行:
;WITH cte AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY validDeliveryDate ASC) AS rn
FROM (
SELECT requests.*, holiday.Date as validDeliveryDate
FROM requests
JOIN holiday
ON requests.TransactionDate < holiday.Date
AND DATEADD(day, 25, requests.TransactionDate) >= holiday.Date
AND holiday.BusinessDay = 'Y' ) v
)
SELECT *
FROM cte
WHERE rn = CASE WHEN critical = 1 THEN 5 ELSE 10 END
不需要迭代 - 工作sqlfiddle here
答案 1 :(得分:1)
计算营业日需要迭代。您添加了几天,然后减去非工作日,然后再次添加。
一种方法是用户定义的功能:
if exists (select * from sys.objects where name ='WorkingDaysFrom' and type = 'FN')
drop function dbo.WorkingDaysFrom
go
create function dbo.WorkingDaysFrom(
@date date
, @days int)
returns date
as
begin
declare @result date = @date
declare @remaining int = @days
while @remaining > 0
begin
set @result = dateadd(day, @remaining, @result)
select @remaining = count(*)
from dbo.Holiday
where [Date] between dateadd(day, 1-@remaining, @result) and @result
and BusinessDay = 'N'
end
return @result
end
go
Live example at SQL Fiddle.打印出来:
TransactionDate Priority DueDate
2013-01-01 Priority 2013-01-16
2013-01-01 Critical 2013-01-09
2013-01-03 Priority 2013-01-17
2013-01-03 Critical 2013-01-10
2013-01-06 Priority 2013-01-18
2013-01-06 Critical 2013-01-11
答案 2 :(得分:0)
我认为以下工作:
select t.*
from (select t.*,
row_number() over (partition by r.requestid, IsWorkDay order by seqdate) as WorkDayNum
from (select r.requestid, r.TransactionDate,
(case when critical = 1 then 5 else 10 end) as DaysToRespond,
dateadd(day, days.seqnum - 1, r.TransactionDate) as seqdate,
(case when h.date is null then 1 else 0 end) as IsWorkDay
from requests r cross join
(select top 20 ROW_NUMBER() over (order by (select NULL)) as seqnum
from information_schema.columns
) days left outer join
holidays h
on dateadd(day, days.seqnum - 1, r.TransactionDate) = h.date
) t
) t
where WorkDayNum = DaysToRespond and IsWorkDay = 1
这是未经测试的,但这是个主意。
好吧,我已对此进行了测试,在这种情况下似乎返回了正确的结果:
with holidays as (
select CAST('2012-01-01' as date) as date union all
select CAST('2012-01-05' as date) as date union all
select CAST('2012-01-06' as date) as date union all
select CAST('2012-01-12' as date) as date union all
select CAST('2012-01-13' as date) as date union all
select CAST('2012-01-19' as date) as date union all
select CAST('2012-01-20' as date) as date
),
requests as (
select 1 as requestId, CAST('2012-01-02' as DATE) as TransactionDate, 1 as Critical
)
select t.*
from (select t.*,
row_number() over (partition by t.requestid, IsWorkDay order by seqdate) as WorkDayNum
from (select r.requestid, r.TransactionDate,
(case when critical = 1 then 5 else 10 end) as DaysToRespond,
dateadd(day, days.seqnum - 1, r.TransactionDate) as seqdate,
(case when h.date is null then 1 else 0 end) as IsWorkDay
from requests r cross join
(select top 20 ROW_NUMBER() over (order by (select NULL)) as seqnum
from INFORMATION_SCHEMA.columns
) days left outer join
holidays h
on dateadd(day, days.seqnum - 1, r.TransactionDate) = h.date
) t
) t
where WorkDayNum = DaysToRespond+1 and IsWorkDay = 1
此查询在事务日期之后创建了20天的序列(足够20?)。然后计算这些天的日期,并将日期与假期表进行比较。
计算请求和工作日与非工作日使用row_number()
分区的天数。要选择的行是工作日和行事日期之后的天数。