T-SQL: CTE row_number over() 第一行的错误结果

T-SQL: CTE row_number over() wrong result for the first line

我有以下 Calendar table 从 2014-01-01 开始。

CREATE TABLE [dbo].[TO_BDB_NOSSCE_ISO_CALENDAR](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [DATE] [date] NOT NULL,
    [YEAR]  AS (datepart(year,[DATE])) PERSISTED,
    [SEMESTER]  AS (case when datepart(month,[DATE])<(7) then '1' else '2' end) PERSISTED NOT NULL,
    [TRIMESTER]  AS (case when datepart(month,[DATE])<(4) then '1' else case when datepart(month,[DATE])<(7) then '2' else case when datepart(month,[DATE])<(10) then '3' else '4' end end end) PERSISTED NOT NULL,
    [MONTH]  AS (case when len(CONVERT([varchar](2),datepart(month,[DATE]),(0)))=(1) then '0'+CONVERT([varchar](2),datepart(month,[DATE]),(0)) else CONVERT([varchar](2),datepart(month,[DATE]),(0)) end) PERSISTED,
    [WEEK]  AS (case when len(CONVERT([varchar](2),datepart(iso_week,[DATE]),(0)))=(1) then '0'+CONVERT([varchar](2),datepart(iso_week,[DATE]),(0)) else CONVERT([varchar](2),datepart(iso_week,[DATE]),(0)) end),
    [DAY]  AS (case when len(CONVERT([varchar](2),datepart(day,[DATE]),(0)))=(1) then '0'+CONVERT([varchar](2),datepart(day,[DATE]),(0)) else CONVERT([varchar](2),datepart(day,[DATE]),(0)) end) PERSISTED,
    [WEEKNUMBER]  AS (datepart(iso_week,[DATE])),
PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

我创建了一个存储过程并将 2014-01-01 添加到 2020-12-31

PROCEDURE [dbo].[sp_INSERT_CALENDAR_DAYS_INVERVAL]

      @Increment              CHAR(1),
      @StartDate              DATETIME,
      @EndDate                DATETIME

AS 
BEGIN    
WITH cteRange (DateRange) AS (
            SELECT @StartDate
            UNION ALL
            SELECT 
                  CASE
                        WHEN @Increment = 'd' THEN DATEADD(dd, 1, DateRange)
                        WHEN @Increment = 'w' THEN DATEADD(ww, 1, DateRange)
                        WHEN @Increment = 'm' THEN DATEADD(mm, 1, DateRange)
                  END
            FROM cteRange
            WHERE DateRange <= 
                  CASE
                        WHEN @Increment = 'd' THEN DATEADD(dd, -1, @EndDate)
                        WHEN @Increment = 'w' THEN DATEADD(ww, -1, @EndDate)
                        WHEN @Increment = 'm' THEN DATEADD(mm, -1, @EndDate)
                  END)

      INSERT INTO [TO_BDB].[dbo].[TO_BDB_NOSSCE_ISO_CALENDAR] ([DATE])
      SELECT DateRange
      FROM cteRange

然后,我想检索绝对周编号,遵循以下逻辑:当前周 - 1(上周),是零周 (0),我想回顾 12 周和回顾 6 周走向未来。

with absolute_weeks as (
  select distinct YEAR, WEEK
from [TO_BDB].[dbo].[TO_BDB_NOSSCE_ISO_CALENDAR]
where DATEADD(week, 0, DATE) between cast(DATEADD(WEEK, -12, GETDATE())  as date) 
                   and cast(DATEADD(WEEK, +6, GETDATE())  as date) 
)
select *, (row_number() over (order by YEAR, WEEK) - 13) as relative_week
from absolute_weeks 
order by YEAR, WEEK;

我得到以下结果,-11 到 +6 周是正确的,但出于某种原因,-12 周不正确。你能解释为什么以及如何修复吗?:

YEAR WEEK relative_week
----------------------
2014    01  -12
2014    43  -11
2014    44  -10
2014    45  -9
2014    46  -8
2014    47  -7
2014    48  -6
2014    49  -5
2014    50  -4
2014    51  -3
2014    52  -2
2015    01  -1
2015    02  0
2015    03  1
2015    04  2
2015    05  3
2015    06  4
2015    07  5
2015    08  6

我认为问题在于e。 g.

DATEPART(ISO_WEEK, '20141231');

实际上 returns 1 - 这是正确的,但会弄乱您的数据。

您可以在基础 table 中为 ISO_Week 的年份使用单独的字段,如下所示:
ISOWEEKYEAR = YEAR(d) + CASE WHEN DATEPART(ISO_WEEK, d) = 1 AND MONTH(d) = 12 THEN 1 ELSE 0 END

正如@KekuSemau 指出的那样,问题与 2014 年 12 月 31 日的 ISOWEEK 为 1 有关。您可以通过在 DATE 或 YEAR,WEEK 订购来解决这个问题;

select year,week,(row_number() over (order by min([DATE])) - 13) as relative_week
from [dbo].[TO_BDB_NOSSCE_ISO_CALENDAR]
where [DATE] between DATEADD(WEEK, -12, GETDATE()) and DATEADD(WEEK, +6, GETDATE()) 
group by year,week
order by min([DATE])

当 'today' 处于一周的开始和结束时,您可能需要对此进行测试,以确保该行为符合您的目的。

顺便说一句 - 将问题正确组合、工作代码、清晰问题等的荣誉 - 似乎没有很多这样的:)

编辑1

这是一种完全不同的方法(我认为 :)

;with cteNumbers as (select top 20 row_number() over (order by object_id) -13 as rn from sys.objects)
select c.year,c.week,n.rn
from cteNumbers n
join [dbo].[TO_BDB_NOSSCE_ISO_CALENDAR] c on c.date = cast(dateadd(d, n.rn*7, getdate()) as date)

您仍然需要对此进行测试,以确保它在一周的开始和结束时 运行 达到您想要的效果。

希望对您有所帮助,

里斯