web-dev-qa-db-ja.com

SQLループWHILE IF BREAK

SQL Server 2012のストアドプロシージャで、いくつかの入れ子構造があります。それらの単層から抜け出したいです。

MsdnのBREAKの説明 https://msdn.Microsoft.com/en-CA/library/ms181271.aspx が私の側にあると思いました。しかし、デバッグのシングルステップで実行しているときに、奇妙な動作が発生します。それは一貫していないので、私は奇妙に言います。時にはそれは私が期待する層にエスケープします。時々それはカップルをスキップします。

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の場合、次の操作はif at stuff6になるはずですよね?たとえば、stuff4に、EXEC呼び出しからストアドプロシージャへのINSERT INTOテーブルが含まれている場合でも、レイヤーからスタックを混乱させることができるものはありませんか?

そして、はい、私はそれが醜いSQLであることを理解しています。ほとんどの命令は2つの一時テーブルの編集であり、そうでなければコードをクリーンアップするストアドプロシージャにそれらをやり取りすることを避けていました。

[〜#〜]編集[〜#〜]

最初にブレークアウトしたい内部IFの周りにダミーのWHILEループを追加することで、希望どおりのルートを確保することができました。しかし、私は本当に私がmsdn情報を誤って解釈しているのか知りたいです。 ENDステートメントがある限り、BREAKはIFから抜け出す必要があると思われます。

出口 wHILEステートメントの最も内側のループ または、WHILEループ内のIF…ELSEステートメント。 ENDキーワードの後に​​出現し、ループの終わりを示すステートメントが実行されます。

9
Hsmith

ドキュメント は少しわかりにくいことに同意します。この行は、IFを中断できることを示唆しているようです。

WHILEステートメント内の最も内側のループまたはWHILEループ内のIF…ELSEステートメントを終了します。 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'

開始テキストと終了テキストのみが印刷されます。 BREAKはWHILEが存在するため、1は出力されません。

この動作はここでも確認できます。

例2

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

このクエリはエラーを返します:

メッセージ135、レベル15、状態1、行4 WHILEステートメントのスコープ外ではBREAKステートメントを使用できません。

7

上記の例のように、 "Start、1、End"を出力するためにIFステートメントから抜け出したい場合は、次のようにします。

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
1
Sean