将表达式转换为数据类型datetime sql的算术溢出错误

时间:2017-09-22 19:19:36

标签: sql-server datetime

我无法弄清楚我把它搞砸了。完整代码:

DECLARE @start_date DATETIME, @end_date DATETIME
DECLARE @Table TABLE (StartDate DATETIME, Enddate DATETIME, WeekNo INT)

SET @start_date = '2017-01-01'
SET @end_date =   '2017-12-31'

INSERT INTO @Table 
    SELECT 
        MIN(dt), MAX(dt), w
    FROM
        (SELECT 
             dt, year(dt) y, DATEPART(week, dt) w
         FROM
             (SELECT 
                  @start_date + (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1) dt
              FROM 
                  sys.columns s1 
              CROSS JOIN
                  sys.columns s2) q
         WHERE 
             dt BETWEEN @start_date AND @end_date) a
GROUP BY 
    y, w

我已经缩小了发生错误的位置。它发生在这里:

SELECT 
    dt, year(dt) y, DATEPART(week, dt) w
FROM
    (SELECT 
         @start_date + (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1) dt
     FROM 
         sys.columns s1 
     CROSS JOIN
         sys.columns s2) q
WHERE 
    dt BETWEEN @start_date AND @end_date

如果我只运行这部分,我会收到错误:

  

将表达式转换为数据类型datetime的算术溢出错误。

我已尝试在这里添加一些CONVERT,试图找到它弄乱的地方,但我无法弄明白。

我见过其他几个有类似问题的人,但this onethis one都没有任何对我有帮助的解决方案。

我是基于集合的SQL编程的新手,我在循环中自学,所以这超出了我的深度。任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:0)

ROW_NUMBER()返回一个bigint值,但是将日期添加到日期仅限于int;因此,如果交叉连接生成的行数超过int的最大值,则会产生该错误消息。

sys.columns中的行数只需要超过代码中用于产生该错误的笛卡尔积的2,147,483,647(> = 46,341 )的平方根。通过重复交叉连接,我能够在更小的sys.columns表上重现相同的错误。 e.g:

  SELECT 
      @start_date + (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1) dt
  FROM 
      sys.columns s1 
  CROSS JOIN
      sys.columns s2
  CROSS JOIN
      sys.columns s3
  CROSS JOIN
      sys.columns s4

避免此问题的一种方法是将row_number()添加到内部查询中的日期,而不是返回row_number(),并使用where子句来避免错误,例如:

DECLARE @start_date DATETIME, @end_date DATETIME
DECLARE @Table TABLE (StartDate DATETIME, Enddate DATETIME, WeekNo INT)

SET @start_date = '2017-01-01'
SET @end_date =   '2017-12-31'

INSERT INTO @Table 
    SELECT 
        MIN(dt), dateadd(day,7,MIN(dt)), w
    FROM
        (SELECT 
             dateadd(day,q.i,@start_date) dt, year(dateadd(day,q.i,@start_date)) y, DATEPART(week, dateadd(day,q.i,@start_date)) w
         FROM (
             SELECT 
                  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 i
              FROM 
                  sys.columns s1 
              CROSS JOIN
                  sys.columns s2
             ) q
         WHERE 
             i <= datediff(day,@start_date,@end_date) 
         ) q2
GROUP BY 
    y, w
;

请注意,有很多选择,特别是因为你真的只想要一周的范围。你可以使用一个永久的&#34;计数表&#34;而不是sys.columns,或使用10行CTE的重复交叉连接来创建所需的行。

此问题Tally Table to insert missing dates between two dates? SQL的已接受答案是使用永久数字表(又名&#34;计数表&#34;)的示例。另外请注意,这样做可以每行添加7天,因此无需将日期分组为几周。

以下是使用CTE重复交叉连接的示例:

DECLARE @start_date DATETIME, @end_date DATETIME
DECLARE @Table TABLE (StartDate DATETIME, Enddate DATETIME, WeekNo INT)

SET @start_date = '2017-01-01'
SET @end_date =   '2017-12-31'

;WITH
cteDigits AS (
    SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL
    SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
),
cteTally AS (
    SELECT [100s].digit * 100 + [10s].digit * 10 + [1s].digit AS i
    FROM cteDigits [1s]
    CROSS JOIN cteDigits [10s]
    CROSS JOIN cteDigits [100s]
)
SELECT
      dateadd(day,q.i*7,@start_date) startDate
    , dateadd(day,(q.i*7)+7,@start_date) endDate
    , year(dateadd(day,q.i*7,@start_date)) y
    , DATEPART(week, dateadd(day,q.i*7,@start_date)) w

FROM cteTally q
WHERE dateadd(day,(q.i*7)+7,@start_date) <= @end_date
ORDER BY q.i