2015-12-21 60 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/ –