2015-12-21 59 views
1

我有一个WHILE循环,应循环30次,但由于某种原因,它似乎只循环15次。SQL循环添加日期到表不工作

SQL:

DECLARE @dateInsert date 
DECLARE @dateLoopTo date 
DECLARE @cnt INT = 0; 
SET @dateInsert='2016-01-01' 
SET @dateLoopTo='2016-01-31' 

WHILE @cnt <= DATEDIFF (d, @dateInsert , @dateLoopTo) 
BEGIN 
print CONVERT(VARCHAR(10),@dateInsert) + ' '+ CONVERT(VARCHAR(2),@cnt) 
SET @dateInsert = DATEADD(d, 1,@dateInsert) 
SET @cnt = @cnt + 1; 
END 

结果:

2016-01-01 0, 
2016-01-02 1, 
2016-01-03 2, 
2016-01-04 3, 
2016-01-05 4, 
2016-01-06 5, 
2016-01-07 6, 
2016-01-08 7, 
2016-01-09 8, 
2016-01-10 9, 
2016-01-11 10, 
2016-01-12 11, 
2016-01-13 12, 
2016-01-14 13, 
2016-01-15 14, 

当我尝试改变DATEDIFF(d,@dateInsert,@dateLoopTo)至30 SQL似乎工作。

WHILE @cnt <= 30-- DATEDIFF (d, @dateInsert , @dateLoopTo) 

是有原因的DATEDIFF(d,@dateInsert,@dateLoopTo)返回30不起作用?

+0

既然你在循环中改变'dateInsert',我看不出'DATEDIFF(d,@dateInsert,@dateLoopTo) '在第一次迭代后仍然会返回30。 –

回答

3

你增加cnt和减少DATEDIFF(增加@dateInsert内环路引起递减差)的同时:

First iteration: cnt = 0; DATEDIFF(d, @dateInsert , @dateLoopTo) = 30 
Second iteration: cnt = 1; DATEDIFF = 29 
Third iteration: cnt = 2; DATEDIFF = 28 

.. 
15th iteration: cnt = 15; DATEDIFF = 15 

LiveDemo

012避免它

一种方法是使用固定的值:

WHILE @cnt <= 30 

或宣布新的变量:

DECLARE @steps INT = DATEDIFF (d, @dateInsert , @dateLoopTo); 

WHILE @cnt <= steps 
... 

LiveDemo2

6

在循环的每个步骤中,您正在递增,这两个都是cnt@DateInsert。因此,你正在走过2秒。

如何编写代码更清楚的:

WHILE @cnt <= 30 
1

你可以做到这一点使用基于集合的这是我建议尽可能循环的方法。要列出两个日期之间的所有日期,你可以这样做:

DECLARE @dateInsert date 
DECLARE @dateLoopTo date 
SET @dateInsert='2016-01-01' 
SET @dateLoopTo='2016-01-31' 

SELECT DATEADD(dd, Number, @dateInsert) 
FROM 
(
    SELECT TOP (DATEDIFF(dd, @dateInsert, @dateLoopTo)) 
     ROW_NUMBER() OVER (ORDER BY o.object_id) - 1 AS Number 
    FROM sys.objects o 
     CROSS JOIN sys.objects o2 
) x 

如果这是常见的活动,我建议你在数据库中创建一个Numbers表格(单柱,以及1与数字一次填充它为x,其中x =大概有多高,你需要数字加一点额外)。并且,根据用例/场景,您还可以创建“日期”表并预先填充 - 这些基本上不需要每次计算日期范围。

1
DECLARE @dateInsert date 
DECLARE @dateLoopTo date 

声明一个计数变量,并使用它,而条件

DECLARE @cnt INT = 0; 
SET @dateInsert='2016-01-01' 
SET @dateLoopTo='2016-01-31' 
DECLARE @Count INT =DATEDIFF (d, @dateInsert , @dateLoopTo) 
WHILE @cnt <= @Count 
BEGIN 
print CONVERT(VARCHAR(10),@dateInsert) + ' '+ CONVERT(VARCHAR(2),@cnt) 
SET @dateInsert = DATEADD(d, 1,@dateInsert) 
SET @cnt = @cnt + 1; 
END 
1

这不是一个答案(因为已经有了通过@Gordon Linoff给出了一个答案),而是一种方式来实现你所需要的,而无需使用WHILE loop,使用更多本机查询。这种变化是类似与@AdaTheDev答案,但没有使用SYS表(可能是你必须限制使用它们)

这里使用递归CTE

DECLARE @dateInsert date = '2016-01-01' 
; 
WITH CTE AS 
      (
      SELECT @dateInsert as Dt, n = 1 
      UNION ALL 
      SELECT DATEADD(day,n,@dateInsert), n = n + 1 
      FROM CTE 
      WHERE DATEADD(day,n,@dateInsert) <= EOMONTH(@dateInsert) 
      ) 

SELECT * FROM CTE 

请注意,EOMONTH()功能可用,因为变种2012版
另请注意,如果您使用recursive cte,那么服务器将使用等于100的默认迭代,如果您需要超过100,那么您需要额外的option (Maxrecursion *here number*)

因此,举例来说,下面的查询会给你365天的序列:

DECLARE @dateInsert date = '2016-01-01' 

;WITH CTE AS 
       (
       SELECT @dateInsert as Dt, n = 1 
       UNION ALL 
       SELECT DATEADD(day,n,@dateInsert) as Dt, n = n + 1 
       FROM CTE 
       WHERE n <=365 
       ) 

    SELECT * FROM CTE 

    OPTION(MAXRECURSION 365) 
+0

在这里要小心。你正在使用一个cte来计算看起来它是基于什么设置的,但它和巧妙隐藏的循环真的是一回事。 http://www.sqlservercentral.com/articles/T-SQL/74118/ –