SQL - 将相同的 ROW_NUMBER 应用于不匹配的行
SQL - Apply same ROW_NUMBER to non-matching rows
我正在使用此查询来计算特定日期范围内的工作日数:
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
如果 DateStarted 和 DateCompleted 都是工作日,这就可以正常工作。如果其中任何一个为空,则结果也为空:
所以我的想法是将下一个工作日 row_number 应用到 weekend/holiday 日期。例如:
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
您可以使用 LEAD()
函数从后续行中提取 RN
值,而不是根据 holiday/weekday 字段排除日期,您可以有条件地应用 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
尝试 DENSE_RANK()
而不是 ROW_NUMBER()。
您仍然需要仅在工作日获取 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
你已经非常接近你想要的累积总和了。类似于:
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;
我正在使用此查询来计算特定日期范围内的工作日数:
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
如果 DateStarted 和 DateCompleted 都是工作日,这就可以正常工作。如果其中任何一个为空,则结果也为空:
所以我的想法是将下一个工作日 row_number 应用到 weekend/holiday 日期。例如:
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
您可以使用 LEAD()
函数从后续行中提取 RN
值,而不是根据 holiday/weekday 字段排除日期,您可以有条件地应用 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
尝试 DENSE_RANK()
而不是 ROW_NUMBER()。
您仍然需要仅在工作日获取 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
你已经非常接近你想要的累积总和了。类似于:
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;