Skip to content

Commit 26615af

Browse files
Merge pull request #35902 from rwestMSFT/rw-1121-fix-10229
Refresh BEGIN...END article (PR 10229)
2 parents e67b30a + 497e2c3 commit 26615af

File tree

1 file changed

+119
-29
lines changed

1 file changed

+119
-29
lines changed

docs/t-sql/language-elements/begin-end-transact-sql.md

Lines changed: 119 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: "BEGIN...END (Transact-SQL)"
33
description: BEGIN...END allows the execution of a group of Transact-SQL statements in a control of flow.
44
author: rwestMSFT
55
ms.author: randolphwest
6-
ms.date: 05/18/2024
6+
ms.date: 11/21/2025
77
ms.service: sql
88
ms.subservice: t-sql
99
ms.topic: reference
@@ -22,22 +22,25 @@ helpviewer_keywords:
2222
- "statements [SQL Server], grouping"
2323
dev_langs:
2424
- "TSQL"
25+
ai-usage: ai-assisted
2526
monikerRange: ">=aps-pdw-2016 || =azuresqldb-current || =azure-sqldw-latest || >=sql-server-2016 || >=sql-server-linux-2017 || =azuresqldb-mi-current || =fabric || =fabric-sqldb"
2627
---
2728
# BEGIN...END (Transact-SQL)
2829

2930
[!INCLUDE [sql-asdb-asdbmi-asa-pdw-fabricse-fabricdw-fabricsqldb](../../includes/applies-to-version/sql-asdb-asdbmi-asa-pdw-fabricse-fabricdw-fabricsqldb.md)]
3031

31-
Encloses a series of [!INCLUDE [tsql](../../includes/tsql-md.md)] statements so that a group of [!INCLUDE [tsql](../../includes/tsql-md.md)] statements can be executed in a logical block of code. `BEGIN` and `END` are control-of-flow language keywords.
32+
Encloses a sequence of [!INCLUDE [tsql](../../includes/tsql-md.md)] statements into a logical block of code. This use of `BEGIN` is unrelated to the `BEGIN TRANSACTION` and `BEGIN ATOMIC` statements.
33+
34+
You can use `BEGIN...END` blocks with a preceding flow-control statement such as `IF`, `ELSE`, and `WHILE`. However, you can also use these blocks without any preceding flow-control statement to group sequences of statements in an organized way. However, each new `BEGIN...END` block doesn't create a new lexical scope.
3235

3336
:::image type="icon" source="../../includes/media/topic-link-icon.svg" border="false"::: [Transact-SQL syntax conventions](../../t-sql/language-elements/transact-sql-syntax-conventions-transact-sql.md)
3437

3538
## Syntax
3639

3740
```syntaxsql
38-
BEGIN
41+
BEGIN [ ; ]
3942
{ sql_statement | statement_block }
40-
END
43+
END [ ; ]
4144
```
4245

4346
## Arguments
@@ -48,61 +51,148 @@ Any valid [!INCLUDE [tsql](../../includes/tsql-md.md)] statement or statement gr
4851

4952
## Remarks
5053

54+
A `BEGIN...END` block must contain at least one statement. If you try to use an empty `BEGIN...END` block, you get a syntax error, even if you use a semicolon after each keyword. You can avoid empty `BEGIN...END` blocks by using a `GOTO` label as a placeholder statement. See [Example C: Use a GOTO label for dynamically generated BEGIN...END blocks](#c-use-a-goto-label-for-dynamically-generated-beginend-blocks).
55+
5156
`BEGIN...END` blocks can be nested.
5257

53-
Although all [!INCLUDE [tsql](../../includes/tsql-md.md)] statements are valid within a `BEGIN...END` block, certain [!INCLUDE [tsql](../../includes/tsql-md.md)] statements shouldn't be grouped together within the same batch, or statement block.
58+
`BEGIN...END` blocks don't define any lexical scope. If you declare a variable within a block, it's visible throughout the parent batch, not just within the block containing the `DECLARE` statement.
59+
60+
You can't use `BEGIN...END` blocks across multiple batches. For example, you can't use the `GO` batch separator inside a `BEGIN...END` block.
61+
62+
Using a `BEGIN...END` block to group statements doesn't mean all statements in the group run atomically. When a batch runs outside a transaction and an error is raised or an exception is thrown by the second statement of a multistatement `BEGIN...END` block, the first statement isn't rolled back.
63+
64+
Semicolons after the `BEGIN` and `END` keywords are [optional but recommended](../../t-sql/language-elements/transact-sql-syntax-conventions-transact-sql.md), except in the following cases:
65+
66+
- You need a semicolon before the `WITH` keyword that starts a [common table expression](../queries/recursive-common-table-expression-transact-sql.md) (CTE).
67+
68+
- You need a semicolon with a `THROW` statement within a block.
69+
70+
- Use a semicolon after `BEGIN` to prevent confusion with the `BEGIN TRANSACTION` or `BEGIN ATOMIC` statements.
71+
72+
- Using a semicolon after `END` ensures that any subsequent statement, particularly a `WITH` keyword or `THROW` statement, doesn't need a preceding semicolon.
73+
74+
Although all [!INCLUDE [tsql](../../includes/tsql-md.md)] statements are valid within a `BEGIN...END` block, you shouldn't group certain [!INCLUDE [tsql](../../includes/tsql-md.md)] statements together within the same batch or statement block. Make sure statements don't conflict with existing Transact-SQL batch requirements.
5475

5576
## Examples
5677

57-
In the following example, `BEGIN` and `END` define a series of [!INCLUDE [tsql](../../includes/tsql-md.md)] statements that execute together. If the `BEGIN...END` block isn't included, both `ROLLBACK TRANSACTION` statements would execute, and both `PRINT` messages would be returned.
78+
[!INCLUDE [article-uses-adventureworks](../../includes/article-uses-adventureworks.md)]
79+
80+
### A. Define a sequence of logically related statements in order
81+
82+
In the following example, `BEGIN` and `END` define sequences of logically related [!INCLUDE [tsql](../../includes/tsql-md.md)] statements to execute in order. The example also shows nested blocks.
5883

5984
```sql
60-
USE AdventureWorks2022;
85+
USE AdventureWorks2025;
6186
GO
6287

63-
BEGIN TRANSACTION
88+
DECLARE @personId AS INT = (
89+
SELECT p.BusinessEntityID
90+
FROM Person.Person AS p
91+
WHERE p.rowguid = { GUID '92C4279F-1207-48A3-8448-4636514EB7E2' }
92+
);
93+
94+
IF (@personId IS NULL)
95+
THROW 50001, 'Person not found.', 1;
96+
97+
/* Concatenate the person's name fields: */;
98+
BEGIN
99+
DECLARE @title AS NVARCHAR (8),
100+
@first AS NVARCHAR (50),
101+
@middle AS NVARCHAR (50),
102+
@last AS NVARCHAR (50),
103+
@suffix AS NVARCHAR (10);
104+
105+
SELECT @title = NULLIF (p.Title, N''),
106+
@first = p.FirstName,
107+
@middle = NULLIF (p.MiddleName, N''),
108+
@last = p.LastName,
109+
@suffix = NULLIF (p.Suffix, N'')
110+
FROM Person.Person AS p
111+
WHERE p.BusinessEntityID = @personId;
112+
113+
DECLARE @nameConcat AS NVARCHAR (255) = CONCAT_WS(N' ', @title, @first, @middle, @last, @suffix);
114+
115+
/* This is a nested BEGIN...END block: */;
116+
BEGIN
117+
DECLARE @emails AS NVARCHAR (MAX) = (
118+
SELECT STRING_AGG(e.EmailAddress, /*separator:*/N'; ')
119+
FROM Person.EmailAddress AS e
120+
WHERE e.BusinessEntityID = @personId
121+
);
122+
123+
SET @nameConcat = CONCAT(@nameConcat, N' (', @emails, N')');
124+
END
125+
END
126+
127+
/* BEGIN...END blocks do not define a lexical scope, so
128+
even though @nameAndEmails is declared above, it is
129+
still in-scope after the END keyword. */
130+
SELECT @nameConcat AS NameAndEmails;
131+
```
132+
133+
### B. Use BEGIN...END in a transaction
134+
135+
In the following example, `BEGIN` and `END` define a series of [!INCLUDE [tsql](../../includes/tsql-md.md)] statements that execute together. If the `BEGIN...END` block isn't included, both `ROLLBACK TRANSACTION` statements execute, and both `PRINT` messages are returned.
136+
137+
```sql
138+
USE AdventureWorks2025;
64139
GO
65140

141+
BEGIN TRANSACTION;
142+
66143
IF @@TRANCOUNT = 0
67-
BEGIN
68-
SELECT FirstName, MiddleName
69-
FROM Person.Person
70-
WHERE LastName = 'Adams';
144+
BEGIN
145+
SELECT FirstName,
146+
MiddleName
147+
FROM Person.Person
148+
WHERE LastName = 'Adams';
71149

72-
ROLLBACK TRANSACTION;
150+
ROLLBACK TRANSACTION;
73151

74-
PRINT N'Rolling back the transaction two times would cause an error.';
75-
END;
152+
PRINT N'Rolling back the transaction two times causes an error.';
153+
END
76154

77155
ROLLBACK TRANSACTION;
78156

79157
PRINT N'Rolled back the transaction.';
80-
GO
81158
```
82159

83-
## Examples: [!INCLUDE [ssazuresynapse-md](../../includes/ssazuresynapse-md.md)] and [!INCLUDE [ssPDW](../../includes/sspdw-md.md)]
160+
### C. Use a GOTO label for dynamically generated BEGIN...END blocks
84161

85-
In the following example, `BEGIN` and `END` define a series of [!INCLUDE [DWsql](../../includes/dwsql-md.md)] statements that run together. If the `BEGIN...END` block isn't included, the following example runs in a continuous loop.
162+
If you generate dynamic Transact-SQL with a `BEGIN...END` block and you want your program to always render the `BEGIN...END` keywords, you can use a `GOTO` label as a placeholder statement to avoid having an empty `BEGIN...END` block.
86163

87164
```sql
88-
-- Uses AdventureWorksDW
165+
BEGIN
166+
unusedLabel:
167+
END
168+
```
89169

90-
DECLARE @Iteration INT = 0;
170+
## Examples: Azure Synapse Analytics and Analytics Platform System (PDW)
91171

92-
WHILE @Iteration < 10
93-
BEGIN
94-
SELECT FirstName,
95-
MiddleName
96-
FROM dbo.DimCustomer
97-
WHERE LastName = 'Adams';
172+
### C. Define a series of statements that run together
173+
174+
In the following example, `BEGIN` and `END` define a series of [!INCLUDE [DWsql](../../includes/dwsql-md.md)] statements that run together.
98175

99-
SET @Iteration += 1;
100-
END;
176+
> [!CAUTION]
177+
> If you remove the `BEGIN` and `END` keywords, the following example runs in an infinite loop. The `WHILE` statement loops only the `SELECT` query, and never reaches the `SET @Iteration += 1` statement.
178+
179+
```sql
180+
-- Uses AdventureWorksDW;
181+
DECLARE @Iteration AS INT = 0;
182+
183+
WHILE @Iteration < 10
184+
BEGIN
185+
SELECT FirstName,
186+
MiddleName
187+
FROM dbo.DimCustomer
188+
WHERE LastName = 'Adams';
189+
SET @Iteration + = 1;
190+
END
101191
```
102192

103193
## Related content
104194

105195
- [ALTER TRIGGER (Transact-SQL)](../statements/alter-trigger-transact-sql.md)
106-
- [Control-of-Flow Language (Transact-SQL)](control-of-flow.md)
196+
- [Control-of-Flow](control-of-flow.md)
107197
- [CREATE TRIGGER (Transact-SQL)](../statements/create-trigger-transact-sql.md)
108198
- [END (BEGIN...END) (Transact-SQL)](end-begin-end-transact-sql.md)

0 commit comments

Comments
 (0)