【问题标题】:SQL loop to add dates to table not workingSQL循环将日期添加到表中不起作用
【发布时间】:2016-03-27 12:29:03
【问题描述】:

我有一个 WHILE 循环,它应该循环 30 次,但由于某种原因,它似乎只循环了 15 次。

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 )

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

【问题讨论】:

  • 由于您在循环中更改了dateInsert,我看不到DATEDIFF (d, @dateInsert , @dateLoopTo ) 在第一次迭代后仍会返回30。

标签: sql-server tsql while-loop


【解决方案1】:

在循环的每一步中,您都在递增 cnt@DateInsert。因此,您以 2 秒的速度通过。

如何更清楚地编写代码:

WHILE @cnt <= 30

【讨论】:

    【解决方案2】:

    你同时递增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

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

    WHILE @cnt <= 30
    

    或声明新变量:

    DECLARE @steps INT = DATEDIFF (d, @dateInsert , @dateLoopTo );
    
    WHILE @cnt <= steps
    ...
    

    LiveDemo2

    【讨论】:

      【解决方案3】:

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

      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=大约需要多高的数字加上一点额外的)。而且,再次根据用例/场景,您还可以创建一个“日期”表并预先填充它 - 这些基本上消除了每次计算日期范围的需要。

      【讨论】:

        【解决方案4】:
        DECLARE @dateInsert date
        DECLARE @dateLoopTo date
        

        声明一个计数变量并在while条件中使用它

        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
        

        【讨论】:

          【解决方案5】:

          这不是一个答案(因为@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)
          

          【讨论】:

          猜你喜欢
          • 2017-08-23
          • 1970-01-01
          • 2016-12-11
          • 2016-06-25
          • 2016-12-03
          • 1970-01-01
          • 2012-10-29
          • 2011-03-28
          • 1970-01-01
          相关资源
          最近更新 更多