为什么这个 DateTime 会溢出?
Why is there an Overflow in this DateTime?
我正在尝试创建一个动态日历,我可以在其中忽略周末并跳到从现在开始的 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
这行得通, 只是替换了生成 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)
我的猜测是 SQL 服务器决定以与您想象的不同的顺序执行操作,实际上是这样的:
;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 > 过滤掉行= 1000(或者数字的分布可能是什么)
我正在尝试创建一个动态日历,我可以在其中忽略周末并跳到从现在开始的 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
这行得通, 只是替换了生成 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)
我的猜测是 SQL 服务器决定以与您想象的不同的顺序执行操作,实际上是这样的:
;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 > 过滤掉行= 1000(或者数字的分布可能是什么)