Oracle PL/SQL GOTO T-SQL · Control-of-Flow · GOTO

T-SQL GOTO Statement

Master T-SQL GOTO and labels — unconditional jumps, when GOTO is legitimate (centralized cleanup paths) vs when to use structured constructs instead, and the modern alternatives (TRY/CATCH, RETURN, WHILE).

GOTO transfers execution unconditionally to a label — a named point in the same batch, stored procedure, or trigger. Unlike IF or WHILE, GOTO doesn't check anything; when execution hits a GOTO, it jumps. Period.

⚠️ Use sparingly. GOTO is the original "spaghetti code" tool. In well-structured T-SQL it's rare — IF, WHILE, BREAK, RETURN, and TRY ... CATCH handle nearly every flow you'd ever need. The two legitimate modern uses are centralized cleanup paths and the rare case of breaking out of nested loops.

A label is an identifier followed by a colon. GOTO label jumps to it.

-- Define a label (the colon is part of it) my_label: -- code at the label -- Jump to the label from anywhere in the same batch GOTO my_label;

Rules

  • The label and the GOTO must live in the same batch, procedure, or trigger.
  • Labels are not case-sensitive in keywords but the name follows normal identifier rules.
  • A GOTO can jump forward (skip ahead) or backward (loop).
  • You can't GOTO into the middle of an IF or WHILE block from outside, or into another procedure.
Example 1 — Manual Loop with GOTO

The classic illustration — though in real code you'd use WHILE instead:

T-SQL — Loop using GOTO
DECLARE @add_number INT = 1;

Test_num:
IF @add_number <= 5
BEGIN
    PRINT 'add_number value: ' + CAST(@add_number AS VARCHAR(10));
    SET @add_number = @add_number + 1;
    GOTO Test_num;
END;
OUTPUT
Messages
Commands completed successfully.
add_number value: 1
add_number value: 2
add_number value: 3
add_number value: 4
add_number value: 5
💡 The WHILE equivalent is shorter and clearer:
WHILE @add_number <= 5
BEGIN
    PRINT @add_number;
    SET @add_number += 1;
END
Use GOTO for things WHILE can't do — not as a substitute for it.
Example 2 — Skipping a Code Block
T-SQL — Forward jump
DECLARE @add_number INT = 10;

IF @add_number = 10
    GOTO skip_section;

PRINT 'This line will be skipped because @add_number is 10';

skip_section:
PRINT 'This line will always execute';
OUTPUT
Messages
Commands completed successfully.
This line will always execute
Example 3 — Centralized Cleanup (Legitimate Use)

One of the few patterns where GOTO genuinely helps: a procedure with multiple early-exit conditions that all need the same cleanup. Without GOTO you'd duplicate the cleanup; with GOTO it lives once.

T-SQL — Single cleanup path
DECLARE @input  INT = -5,
        @result INT;

-- Validation step 1
IF @input IS NULL
BEGIN
    PRINT 'Error: input is NULL';
    GOTO cleanup;
END

-- Validation step 2
IF @input < 0
BEGIN
    PRINT 'Error: input is negative';
    GOTO cleanup;
END

-- Main work (only reached if validations passed)
SET @result = @input * 2;
PRINT 'Result: ' + CAST(@result AS VARCHAR(10));

cleanup:
PRINT 'Cleanup done. Goodbye.';
OUTPUT
Messages
Commands completed successfully.
Error: input is negative
Cleanup done. Goodbye.
📌 Modern alternative. Most production code uses TRY ... CATCH for error paths and RETURN for early exits — those handle the same use case more clearly. Reach for GOTO only when those don't fit.
Instead of GOTO for...Use
LoopingWHILE
Exiting a loop earlyBREAK
Skipping the rest of an iterationCONTINUE
Exiting the entire procedureRETURN
Reacting to errorsTRY ... CATCH
Conditional branchingIF ... ELSE

If your control flow can be expressed cleanly with the structured constructs above, prefer them — code with GOTO is harder to follow because execution can jump anywhere.

  • GOTO label jumps unconditionally to a labeled point in the same batch or procedure.
  • Labels are identifiers ending with a colon; they live alongside other statements.
  • Modern T-SQL replaces almost all GOTO uses with structured constructs — WHILE, BREAK, CONTINUE, RETURN, TRY ... CATCH.
  • The legitimate niche: a single shared cleanup path at the bottom of a procedure that several validation failures all jump to.
  • Avoid jumping backward for loops, jumping across nested control structures, or generally writing code where the next line of execution isn't obvious from reading top-to-bottom.