我无法弄清楚我把它搞砸了。完整代码:
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 one或this one都没有任何对我有帮助的解决方案。
我是基于集合的SQL编程的新手,我在循环中自学,所以这超出了我的深度。任何帮助表示赞赏。
答案 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