BREAK and CONTINUE are two forms of loop control, which I have seen under-used lately.  I think it is time to revisit how these two statements can help you make more maintainable and readable code, with some basic comments where needed.

BREAK is used to prematurely exit a loop sequence, be it a WHILE loop, or a FOR loop.  CONTINUE is used to force the next iteration of the loop.  These two keywords exist verbatim in SQL and C/C#/Java, but all languages have them in some dialect.

A great way to understand easier maintenance is the venerable CURSOR in SQL.  Generally, a cursor loop is written as such:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
DECLARE @nValue1 INT, @nValueA INT, @nValue42 INT
 
DECLARE curSingleRow CURSOR FOR SELECT Value1, ValueA, Value42 FROM TABLE
 
OPEN curSingleRow
 
FETCH NEXT FROM curSingleRow INTO @nValue1, @nValueA, @nValue42
WHILE @@FETCH_STATUS = 0 BEGIN
 
/* ...
process the single row here
... */
 
/* ... get the next row to process ... */
FETCH NEXT FROM curSingleRow INTO @nValue1, @nValueA, @nValue42
END
 
CLOSE curSingleRow
DEALLOCATE curSingleRow

This code is very familiar to SQL developers. The @@FETCH_STATUS value becomes non-zero if no further records are available, or some other error occurs.  The problem with this code is that if a column in the select statement is added, dropped or changed, there are three points of maintenance: the declare statement, and two fetch statements.  If the loop is rather large, the last fetch statement is going to be a long way down.

Consider the following change to the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
DECLARE @nValue1 INT, @nValueA INT, @nValue42 INT
 
DECLARE curSingleRow CURSOR FOR SELECT Value1, ValueA, Value42 FROM TABLE
 
OPEN curSingleRow
 
/* (1=1) means always true: intentional. Exit is forced within the loop itself */
WHILE (1=1) BEGIN
 
/* ... get the first or next row to process ... */
FETCH NEXT FROM curSingleRow INTO @nValue1, @nValueA, @nValue42
IF @@FETCH_STATUS <> 0 BREAK
 
/* ...
process the single row here
... */
 
END
 
CLOSE curSingleRow
DEALLOCATE curSingleRow

The change makes the WHILE loop appear infinite, but it simply guarantees that the execution will always enter the loop.  Notice that I explicitly put a comment there to ensure that anyone maintaining the code understands the intent.  Since the FETCH statement is identical for the first and successive records, that is what will occur with the same statement–on the initial entry and upon successive iterations.  The IF statement checks for either no more records or an error condition, and will trigger the BREAK statement when that happens.  Otherwise, the code below the IF statement continues to execute, and will iterate back to the WHILE statement.  This scenario also covers the case of no rows returned by the SELECT statement.  The BREAK statement immediately exits the WHILE loop, executing the CLOSE statement.

This now reduces maintenance to a DECLARE statement and a single FETCH statement.  To me, the code is also more readable.  My first instinct reading the original code is to question why the FETCH statement was repeated in the original code (it’s just because of the way my brain works).

BREAK and CONTINUE are extremely useful for creating short-circuiting logic, within a main logic condition.  Example: Say you needed to parse a set of command arguments, where certain argument combinations would cause remaining arguments to be ignored, or cause the single argument to be ignored.  The code could be written like this.