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

时间:2015-01-16 12:05:57

标签: sql-server tsql date sql-server-2008-r2 common-table-expression

我从Calendar开始有以下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

2 个答案:

答案 0 :(得分:2)

我认为问题在于e。克。

DATEPART(ISO_WEEK, '20141231');

实际上会返回1 - 这是正确的但会弄乱你的数据。

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

答案 1 :(得分:1)

正如@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])

当“今天”在一周的开始和结束时,您可能需要对此进行测试,以确保该行为符合您的目的。

顺便说一下 - 将问题放在一起,工作代码,明确问题等方面的荣誉 - 似乎没有多少像这样:)

<强> EDIT1

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

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

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

希望这有帮助,

里斯