我正在尝试创建一个动态日历,我可以忽略周末并从现在起45天内跳过
Declare @StartDate Date = '12/01/2015'
;With NumberList AS
(
Select *
From
(
Select Rank() Over(Order By S1.Id, S2.Id) Number
From master.dbo.SysObjects S1, master.dbo.SysObjects S2
) XX
Where Number < 1000
)
,FullCalendar as
(
select Cast (dateadd(day, number, DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)) as Date) CalendarDate
from Numberlist
)
,CalendarWithNumbers As
(
Select CalendarDate, Row_Number() Over (Order By CalendarDate) DayNumber
From FullCalendar
Where ((DATEPART(dw, CalendarDate) + @@DATEFIRST) % 7) NOT IN (0, 1)
)
,StartingDate as
(
Select *
From CalendarWithNumbers
Where 1=1
And CalendarDate = @StartDate
)
Select @StartDate StartDate, CalendarDate NDaysFromNow, DayNumber
From CalendarWithNumbers
Where DayNumber = (Select DayNumber + 45 from StartingDate)
但是,我收到了一个溢出错误
StartDate NDaysFromNow DayNumber
---------- ------------ --------------------
Msg 517, Level 16, State 1, Line 3
Adding a value to a 'datetime' column caused an overflow.
但是,我只在我的第一个CTE中选择前1000个数字,而我的开始日期是一年中的第一天。 1/1/2015
+ 1000天仅上升到9/26/2017
。
为什么我收到溢出错误?
我在代码中添加了一条注释,以便于理解。
Declare @StartDate Date = '12/01/2015'
;With NumberList AS
(
/* Get a list of 1000 Numbers*/
Select *
From
(
/*
Get a sequence of Numbers (I get 2071*2071 = 4289041)
I may need to go up to 50 years in the future so I have to do a cartesian product (same as Cross Join)
However, this is where it fails
*/
Select Rank() Over(Order By S1.Id, S2.Id) Number
From master.dbo.SysObjects S1, master.dbo.SysObjects S2
/*
comment out the Select ABOVE
Uncomment the Select BELOW
It works!!
Probably because now the sequencing only goes up to 2071
*/
--Select Rank() Over(Order By S1.Id) Number
--From master.dbo.SysObjects S1
) XX
Where Number < 1000
)
--Select * From NumberList
--UnComment just the line above, and comment everything below to see the result just up to there
,FullCalendar as
(
/* Take the first day of the year, and add 1 to 1000 days to it
It starts from 2015-01-02 (bug needs to be fixed so it starts from 2015-01-01)
It ends at 2017-09-26
*/
select Cast (dateadd(day, number, DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)) as Date) CalendarDate
from Numberlist
)
--Select * From FullCalendar
--UnComment just the line above, and comment everything below to see the result just up to there
,CalendarWithNumbers As
(
/*
WHERE CLAUSE exclude the weekend days
ROW_NUMBER now number each day sequentially and save it in the DayNumber column
*/
Select CalendarDate, Row_Number() Over (Order By CalendarDate) DayNumber
From FullCalendar
Where ((DATEPART(dw, CalendarDate) + @@DATEFIRST) % 7) NOT IN (0, 1)
)
--Select * From CalendarWithNumbers
--UnComment just the line above, and comment everything below to see the result just up to there
,StartingDate as
(
/* Get the DayNumber (from the query above) for the starting day*/
Select *
From CalendarWithNumbers
Where 1=1
And CalendarDate = @StartDate
)
--Select * From StartingDate
--UnComment just the line above, and comment everything below to see the result just up to there
/*
Now calculate 45 days from the daynumber in starting date
that gives you the value for NDays from StartDate
*/
Select
SD.CalendarDate,
Sd.DayNumber,
CWN.CalendarDate NDaysFromNow,
CWN.DayNumber
From CalendarWithNumbers CWN
Inner Join StartingDate SD
On CWN.DayNumber = SD.DayNumber + 345
答案 0 :(得分:1)
这适用于,,,,只是替换了生成NumberList
Declare @StartDate Date = '12/01/2015' --<-- I would use '20151201'
;With NumberList AS
(
Select *
From
(
Select ROW_NUMBER() Over(Order By (SELECT NULL)) Number
From master..spt_values S1
) XX
Where Number < 1000
)
,FullCalendar as
(
select Cast (dateadd(day, number, DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)) as Date) CalendarDate
from Numberlist
)
,CalendarWithNumbers As
(
Select CalendarDate, Row_Number() Over (Order By CalendarDate) DayNumber
From FullCalendar
Where ((DATEPART(dw, CalendarDate) + @@DATEFIRST) % 7) NOT IN (0, 1)
)
,StartingDate as
(
Select *
From CalendarWithNumbers
Where 1=1
And CalendarDate = @StartDate
)
Select @StartDate StartDate, CalendarDate NDaysFromNow, DayNumber
From CalendarWithNumbers
Where DayNumber = (Select DayNumber + 45 from StartingDate)
答案 1 :(得分:1)
我的猜测是SQL Server决定按照你想象的不同顺序做事,实际上是这样的:
;With NumberList AS
(
Select *
From
(
Select Rank() Over(Order By S1.Id, S2.Id) Number
From master.dbo.SysObjects S1, master.dbo.SysObjects S2
) XX
Where Number < 1000
)
在此之前不会过滤到999行;
select Cast (dateadd(day, number, DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)) as Date) CalendarDate
即使这是你期望发生的事情。如果将其更改为此,则错误将消失:
;With NumberList AS
(
Select top 999 row_number() Over(Order By (select null)) Number
From master.dbo.SysObjects S1 cross join master.dbo.SysObjects S2
)
使用top
通常是一个更好的选择,因为这可以让优化器更好地了解实际来自那里的行数,而不必决定何时使用Number&gt; =来过滤行1000(或数字的分布可能是什么)