【问题标题】:SQL loop WHILE IF BREAKSQL 循环 WHILE IF BREAK
【发布时间】:2015-12-15 19:09:03
【问题描述】:

在 SQL Server 2012 存储过程中,我有几个嵌套结构。我想突破其中的一层。

我认为 msdn https://msdn.microsoft.com/en-CA/library/ms181271.aspx 中对 BREAK 的描述是站在我这边的。但是我在通过调试单步运行它时遇到了一些奇怪的行为。我说奇怪是因为它不一致。有时它会逃到我期望的层。有时它会跳过几个。

WHILE ... BEGIN
  stuff1
  IF...BEGIN
    stuff2
    WHILE ... BEGIN
      stuff3
      IF .... BEGIN
        stuff4
        IF @NumberRecords=0 BREAK
        stuff5
      END
      --stuff6
      if @NumberRecords=0 and @loopBOMRowCount=@ResultsSOloopstart-1 break
      --on the last occasion I observed, @loopBOMRowCount was 6 and @ResultsSOloopstart 71 and it never highlighted this section, either way

      SET @loopBOMRowCount = @loopBOMRowCount + 1
    END
    stuff7 --nothing actually here
  END
  --stuff8
  SET @periodloopcount=@periodloopcount+1 
  --this is where it ended up highlighting on that last occasion
END
stuff9

所以如果 NumberRecords=0,那么下一个操作应该是 stuff6 的 if,对吗?即使 stuff4 包括从 EXEC 调用到存储过程的 INSERT INTO 表?应该没有什么能将堆栈从它的层中混淆出来?

是的,我意识到那是丑陋的 SQL。大多数指令是对两个临时表的编辑,我避免将它们来回传递给本来会清理代码的存储过程。

编辑

我设法让它按照我想要的方式路由,方法是在我想首先突破的内部 IF 周围添加一个虚拟 WHILE 循环。但我真的很想知道我是如何误解 msdn 信息的。似乎 BREAK 应该脱离 IF,只要它有 END 语句。

退出WHILE 语句中的最内层循环 或WHILE 循环中的IF…ELSE 语句。任何出现在 END 关键字之后的语句,标记循环的结束,都会被执行。

【问题讨论】:

  • AFAIK 它应该执行stuff7 而不是stuff6BREAK 打破循环。来自文档:出现在 END 关键字之后的任何语句,标记循环的结束,都会被执行。
  • 那么与 Break 的 IF 之上的 IF 一起的 END 不算数? IF x=0 BREAK stuff5 END stuff6
  • 一旦内部子循环满足父循环的下一次迭代,您是否忘记重置变量值?在不包括这些条件的情况下很难判断发生了什么
  • 我的假设基于此:退出 ... 或 WHILE 循环内的 IF...ELSE 语句(来自 msdn)
  • @SergioS,可能,这就是我运行调试的原因,但它不能解释(我不认为)观察调试行从突出显示 BREAK (我预期的条件为那一刻)在我按下 F11 后突出显示 stuff8 部分的第一行。我将在问题中添加部分

标签: sql-server control-structure


【解决方案1】:

我同意documentation 有点混乱。这行似乎表明您可以脱离 IF。

退出 WHILE 语句或 IF...ELSE 语句中的最内层循环 在 WHILE 循环内。出现在 END 关键字之后的任何语句, 标记循环的结束,被执行。 BREAK 经常出现,但是 并非总是如此,由 IF 测试开始。

然而事实并非如此。 BREAK 从其位置退出最里面的 WHILE。文档的关键部分是“所有出现在 END 关键字之后的语句,标记循环结束,都会被执行。”。

这个例子证明了这一点。

示例 1

DECLARE @X INT = 1;

PRINT 'Start'

/* WHILE loop required to use BREAK.
 */
WHILE @X = 1
BEGIN

    /* Outer IF.
     */
    IF 1 = 1 
    BEGIN
        /* Inner IF.
         */
        IF 2 = 2
        BEGIN
            BREAK
            PRINT '2'
        END

        PRINT '1'
    END

    SET @X = @X + 1;
END

PRINT 'End'

只打印开始和结束文本。 1 未打印,因为 BREAK 存在于 WHILE。

您还可以在此处查看此行为:

示例 2

/* Anti-Pattern.
 * Breaking outside a WHILE is not allowed.
 */
IF 1 = 1 
BEGIN
    BREAK 
    PRINT 1
END

此查询返回错误:

消息 135,级别 15,状态 1,行 4 不能使用 BREAK 语句 在 WHILE 语句的范围之外。

【讨论】:

  • 哈。最后一个 sn-p 对我来说是一个很好的测试方法,不会打扰你们。很高兴我不是唯一一个看到歧义的人。谢谢。
【解决方案2】:

如果你真的想跳出IF语句,像上面的例子一样打印“Start,1,End”,你可以这样做

DECLARE @X INT = 1;

PRINT 'Start'

/* WHILE loop required to use BREAK.
 */
WHILE @X = 1
BEGIN

    /* Outer IF.
     */
    IF 1 = 1 
    BEGIN
        /* Inner IF.
         */
        IF 2 = 2
        BEGIN
            GOTO skip2
            PRINT '2'
        END
        skip2:

        PRINT '1'
    END

    SET @X = @X + 1;
END

PRINT 'End'

现在您可以使用它来处理 OP 的示例,使用以下方法

WHILE ... BEGIN
  stuff1
  IF...BEGIN
    stuff2
    WHILE ... BEGIN
      stuff3
      IF .... BEGIN
        stuff4
        IF @NumberRecords=0
            GOTO startstuff6
        stuff5
      END
      startstuff6:
      --stuff6
      if @NumberRecords=0 and @loopBOMRowCount=@ResultsSOloopstart-1
        GOTO startstuff7
      --on the last occasion I observed, @loopBOMRowCount was 6 and @ResultsSOloopstart 71 and it never highlighted this section, either way

      SET @loopBOMRowCount = @loopBOMRowCount + 1
    END
    startstuff7:
    stuff7 --nothing actually here
  END
  --stuff8
  SET @periodloopcount=@periodloopcount+1 
  --this is where it ended up highlighting on that last occasion
END
stuff9

这通常被认为是更好的方法来反转你的布尔逻辑,例如:

IF NOT @NumberRecords=0
BEGIN
        stuff5
END

【讨论】: