SQL Server 2008中的Do while循环

137

在SQL Server 2008中是否有实现“do while”循环的方法?


5
Rahul 给出的答案是正确的,但你到底想要实现什么目标?与基于集合的解决方案相比,循环操作代价高昂。也许可以完全避免使用循环。 - Lieven Keersmaekers
如果可能的话,请尽量避免使用循环,我估计95%或更多的时间都可以避免使用它们。循环和游标会降低性能,不应该由没有至少五年性能调优经验的有经验的DBA编写。 - HLGEM
1
Er. HLGEM 有点夸张了,只要不是遍历表中的每一行,循环和游标实际上非常好用。如果你有一个类别或站点列表或其他相对高级的东西,那么循环可能是运行查询最有效的方式。 - Geoff Griswald
5个回答

201
我不确定在MS SQL Server 2008中是否有DO-WHILE循环,但您可以更改WHILE循环逻辑,以便像使用DO-WHILE循环一样使用。

这里的示例来自于此:http://blog.sqlauthority.com/2007/10/24/sql-server-simple-example-of-while-loop-with-continue-and-break-keywords/

  • WHILE循环示例:

  • DECLARE @intFlag INT
    SET @intFlag = 1
    WHILE (@intFlag <=5)
    BEGIN
        PRINT @intFlag
        SET @intFlag = @intFlag + 1
    END
    GO
    

    结果集:

    1
    2
    3
    4
    5
    
  • 使用BREAK关键字的WHILE循环示例

  • DECLARE @intFlag INT
    SET @intFlag = 1
    WHILE (@intFlag <=5)
    BEGIN
        PRINT @intFlag
        SET @intFlag = @intFlag + 1
        IF @intFlag = 4
            BREAK;
    END
    GO
    

    结果集:

    1
    2
    3
    
  • 带有CONTINUE和BREAK关键字的WHILE循环示例

  • DECLARE @intFlag INT
    SET @intFlag = 1
    WHILE (@intFlag <=5)
    BEGIN
        PRINT @intFlag
        SET @intFlag = @intFlag + 1
        CONTINUE;
        IF @intFlag = 4 -- This will never executed
            BREAK;
    END
    GO
    

    结果集:

    1
    2
    3
    4
    5
    

    尽量避免在数据库层面使用循环参考链接


    20
    这里给出了相同的示例,你是这个网站的作者吗?http://blog.sqlauthority.com/2007/10/24/sql-server-simple-example-of-while-loop-with-continue-and-break-keywords/ - anar khalilov
    2
    他并不是,但这有什么关系吗?正确答案已经给出。链接到另一个网站很麻烦,复制粘贴答案到这里会更有帮助。 - Geoff Griswald
    @GeoffGriswald,如果你的作品被人无署名地复制,你可能会有不同的看法。是的,在这里粘贴答案是有帮助的,但是添加一个链接以表示致谢只是一种常见的礼貌。 - undefined

    69

    如果您不太反感GOTO关键字,它可以用于模拟T-SQL中的DO/WHILE。考虑以下以伪代码编写的相当荒谬的示例:

    SET I=1
    DO
     PRINT I
     SET I=I+1
    WHILE I<=10
    

    以下是使用goto的等效T-SQL代码:

    DECLARE @I INT=1;
    START:                -- DO
      PRINT @I;
      SET @I+=1;
    IF @I<=10 GOTO START; -- WHILE @I<=10
    

    注意使用 GOTO 实现和原始 DO / WHILE 伪代码之间的一对一映射关系。一个类似的使用 WHILE 循环的实现看起来像:

    DECLARE @I INT=1;
    WHILE (1=1)              -- DO
     BEGIN
      PRINT @I;
      SET @I+=1;
      IF NOT (@I<=10) BREAK; -- WHILE @I<=10
     END
    

    现在,当然可以将这个特定的例子重写为简单的WHILE循环,因为这不是DO/WHILE结构的好候选对象。重点是示例简洁性而不是适用性,因为需要DO/WHILE的合法情况很少。


    REPEAT / UNTIL,有人试过(在T-SQL中不起作用)?

    SET I=1
    REPEAT
      PRINT I
      SET I=I+1
    UNTIL I>10
    

    ...以及在T-SQL中基于GOTO的解决方案:

    DECLARE @I INT=1;
    START:                    -- REPEAT
      PRINT @I;
      SET @I+=1;
    IF NOT(@I>10) GOTO START; -- UNTIL @I>10
    

    通过创造性地运用 GOTO 和逻辑反转,使用 NOT 关键字,原始伪代码和基于 GOTO 的解决方案之间有非常密切的关系。使用 WHILE 循环的类似解决方案如下:

    DECLARE @I INT=1;
    WHILE (1=1)       -- REPEAT
     BEGIN
      PRINT @I;
      SET @I+=1;
      IF @I>10 BREAK; -- UNTIL @I>10
     END
    

    可以提出这样的观点,对于REPEAT/UNTIL的情况,基于WHILE的解决方案更简单,因为if条件没有被翻转。另一方面,它也更加冗长。

    如果不考虑使用GOTO所带来的鄙视,这些或许是惯用的解决方案,只有在T-SQL代码中确实需要这些特定的(邪恶的)循环结构以便清晰易懂时才会用到。

    请自行谨慎使用,避免使用备受诟病的GOTO而惹怒您的同事开发人员。


    7
    +1:这个回答比被采纳的回答更好地回答了问题。 - Louis Kottmann
    我更喜欢使用WHILE(1 = 1)方法,因为它在不使用可怕的GOTO的情况下实现了功能上的目的。 - kad81
    两种方法都是有效的。我喜欢使用GOTO的伪循环,这非常聪明。 - Geoff Griswald

    22

    我似乎记得读过这篇文章超过一次,而答案只是 接近 我所需的。

    通常当我认为我需要在 T-SQL 中使用 DO WHILE 时,那是因为我正在迭代一个光标,并且主要寻求最佳清晰度(而不是最佳速度)。在 T-SQL 中,这似乎适合使用 WHILE TRUE / IF BREAK

    如果这就是您来到这里的情况,则此代码片段可能会节省您一些时间。否则,欢迎回来,我。现在我可以确定我来过这里不止一次。 :)

    DECLARE Id INT, @Title VARCHAR(50)
    DECLARE Iterator CURSOR FORWARD_ONLY FOR
    SELECT Id, Title FROM dbo.SourceTable
    OPEN Iterator
    WHILE 1=1 BEGIN
        FETCH NEXT FROM @InputTable INTO @Id, @Title
        IF @@FETCH_STATUS < 0 BREAK
        PRINT 'Do something with ' + @Title
    END
    CLOSE Iterator
    DEALLOCATE Iterator
    

    不幸的是,T-SQL似乎没有更清晰地定义循环操作的方法,除了这个无限循环。


    使用游标从来都不是一个好的选择,因为它需要比实际所需更多的资源。 - greektreat
    12
    @greektreat: 感谢你的投票 :), 但我有些困惑!如果“光标从来不是一个不好的选择”,那么它必须总是是一个好的选择,所以为什么会被投票否决?不过说真的,显然光标有很多实际的用途:针对私有表,针对需要清晰/简洁 > 性能的小操作,针对没有确定性操作的维护任务,针对某些操作必须作为原子事务进行等等。在我最近的情况下,我正在处理一个传入的表变量,这个变量只对我的存储过程私有。绝对的陈词滥调从来都不是一个好主意! - shannon
    7
    总之,有时迭代数据是唯一的选择。我想你仍然可以争论在这种情况下它不是一个好选择,但这并不意味着这种代码是不必要的或者需要被踩。 - shannon
    1
    我认为互联网上有一支狂热的人群,他们非常愤怒其他人在SQL中使用循环和游标。在这个网站上,如果你甚至提到在SQL中使用循环,大约30秒后你的收件箱就会被无知的人淹没,告诉你在任何情况下都不要使用它们... - Geoff Griswald

    4

    如果您希望代码更易读,也可以使用退出变量:

    DECLARE @Flag int = 0
    DECLARE @Done bit = 0
    
    WHILE @Done = 0 BEGIN
        SET @Flag = @Flag + 1
        PRINT @Flag
        IF @Flag >= 5 SET @Done = 1
    END
    

    当你有一个更复杂的循环并且试图跟踪逻辑时,这可能更相关。正如所述,循环是昂贵的,因此如果可以,请尝试使用其他方法。


    我的意思是...我们生活在一个时代,我们的数据库服务器在任何给定时刻都有10到20个CPU核心处于空闲状态,而我们的存储控制器可用带宽以千兆位为单位进行测量,所以我不确定这种传统的“智慧”即“循环=糟糕”是否仍然适用。 - Geoff Griswald

    1
    只有 While Loop 是 SQL Server 官方支持的。已经有一个关于 DO while 循环的 答案。我将详细说明如何在 SQL Server 中实现不同类型的循环。
    如果您知道必须完成循环的第一次迭代,那么您可以尝试 SQL Server 的 DO..WHILE 或 REPEAT..UNTIL 版本。
    DO..WHILE 循环
    DECLARE @X INT=1;
    
    WAY:  --> Here the  DO statement
    
      PRINT @X;
    
      SET @X += 1;
    
    IF @X<=10 GOTO WAY;
    

    REPEAT..UNTIL循环

    DECLARE @X INT = 1;
    
    WAY:  -- Here the REPEAT statement
    
      PRINT @X;
    
      SET @X += 1;
    
    IFNOT(@X > 10) GOTO WAY;
    

    FOR循环

    DECLARE @cnt INT = 0;
    
    WHILE @cnt < 10
    BEGIN
       PRINT 'Inside FOR LOOP';
       SET @cnt = @cnt + 1;
    END;
    
    PRINT 'Done FOR LOOP';
    

    参考资料


    这似乎是从https://dev59.com/Pm025IYBdhLWcg3wVkef#46362450复制粘贴并重新排列的。 - SecretAgentMan
    @SecretAgentMan:两个答案回答了不同的问题。两个答案都提供了额外的数据。 - Somnath Muluk

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接