我使用此查询来计算特定日期范围的营业日数:
WITH cte AS (
SELECT [Date] AS WorkingDay,
ROW_NUMBER() OVER (ORDER BY [Date] ASC) AS RN
FROM DimDate
WHERE IsHolidayUSA = 0
AND IsWeekday = 1
)
SELECT
DateStarted,
DateCompleted,
c2.RN - c1.RN AS CycleTime
FROM MyTable t
INNER JOIN cte c1
ON t.DateStarted=c1.WorkingDay
INNER JOIN cte c2
ON t.DateCompleted=c2.WorkingDay
如果DateStartedand和DateCompleted都是工作日,则此工作正常。如果其中任何一个为null,则result也为null:
因此,我们的想法是将以下工作日row_number应用于周末/假日日期。例如:
Date RN
2015-02-23 1 -- Mon
2015-02-24 2 -- Tue
2015-02-25 3 -- Wed
2015-02-26 4 -- Thu
2015-02-27 5 -- Fri
2015-02-28 6 -- Sat (applied row number of next business day)
2015-03-01 6 -- Sun (applied row number of next business day)
2015-03-02 6 -- Mon
2015-03-03 7 -- Tue
2015-03-04 8 -- Wed
2015-03-05 9 -- Thu
编辑:
提取ROW_NUMBER查询并指向需要处理的部分:
select Date as WorkingDay,
RN =
CASE WHEN IsHolidayUSA = 0 AND IsWeekday = 1
THEN ROW_NUMBER() OVER (ORDER BY [Date] ASC)
ELSE 1 -- need to modify this one
END
from DimDate
答案 0 :(得分:1)
您可以使用LEAD()
函数从后续行中提取RN
值,而不是根据假日/工作日字段排除日期,您可以有条件地应用ROW_NUMBER()
他们:
;WITH cte AS (SELECT [Date] AS WorkingDay
, CASE WHEN IsHolidayUSA <> 0 AND IsWeekday <> 1 THEN NULL
ELSE ROW_NUMBER() OVER(PARTITION BY CASE WHEN IsHolidayUSA <> 0 AND IsWeekday <> 1 THEN 1 END ORDER BY [Date])
END AS RN
FROM DimDate
)
SELECT *,RN = COALESCE(RN,LEAD(RN,1) OVER(ORDER BY WorkingDay) ,LEAD(RN,2) OVER(ORDER BY WorkingDay))
FROM cte
ORDER BY WorkingDay
如果需要,您可以添加更多LEAD()
个功能以适应3或4天的周末。
这是一个在不存在的表上演示的工作示例:
;WITH cal AS (SELECT CAST('2013-03-01' AS DATE) dt
UNION ALL
SELECT DATEADD(DAY,1,dt)
FROM cal
WHERE dt < '2013-03-31')
,RN AS (SELECT *,CASE WHEN DATENAME(WEEKDAY,dt) IN ('Saturday','Sunday') THEN NULL
ELSE ROW_NUMBER() OVER(PARTITION BY CASE WHEN DATENAME(WEEKDAY,dt) IN ('Saturday','Sunday') THEN 1 END ORDER BY dt)
END AS RN
FROM cal
)
SELECT *,RN = COALESCE(RN,LEAD(RN,1) OVER(ORDER BY dt) ,LEAD(RN,2) OVER(ORDER BY dt))
FROM RN
ORDER BY dt
答案 1 :(得分:1)
你仍然只需要在工作日获得row_number(),但诀窍是然后将所有日期加入到这个工作日cte,并查找非工作日的下一个工作日。 (混淆)...
with dn as (
select
*,
IsWorkingDay = cast(case when IsHolidayUSA = 0 AND IsWeekday = 1 then 1 else 0 end as bit)
from DimDate
where [Date] between '2/23/2015' and '3/5/2015'
), wd as (
select
[Date],
WorkingDayNum = row_number() OVER (ORDER BY [Date] ASC)
from dn
where IsWorkingDay = 1
), d as (
select
dn.[Date],
[WorkingDayNum] = coalesce(wd.WorkingDayNum, n.WorkingDayNum)
from
dn
left outer join wd on wd.[Date] = dn.[Date]
outer apply (
select top 1 wd.WorkingDayNum
from wd
where wd.[Date] > dn.[Date]
order by wd.[Date]
) n
)
select * from d order by Date
答案 2 :(得分:1)
你可以通过累积总和得到你想要的东西。类似的东西:
select Date as WorkingDay,
SUM(case when IsHolidayUSA = 0 and IsWeekday = 1 then 1 else 0 end) over
(order by [date] asc) as rn
from DimDate;
问题在于,这给周末和假日提供的数字等于之前的工作日,而不是下一个。因此,在某些情况下通过添加1来修改它:
select Date as WorkingDay,
(SUM(case when IsHolidayUSA = 0 and IsWeekday = 1 then 1 else 0 end) over
(order by [date] asc)
(case when IsHolidayUSA = 0 and IsWeekday = 1 then 0 else 1 end)) as rn
from DimDate;
答案 3 :(得分:0)
尝试DENSE_RANK()
而不是ROW_NUMBER()。