开始。。。结束(Transact-SQL)

适用于:SQL ServerAzure SQL 数据库Azure SQL 托管实例Azure Synapse Analytics分析平台系统(PDW)Microsoft Fabric 中的 SQL 分析终结点Microsoft Fabric 中的仓库Microsoft Fabric 中的 SQL 数据库

将 Transact-SQL 语句序列包裹在逻辑代码块中。 这种用法 BEGINBEGIN TRANSACTIONBEGIN ATOMIC 语句无关。

你可以使用 BEGIN...END 带有前置流量控制语句的块,如 IFELSEWHILE和 。 不过,你也可以在没有任何前置流量控制语句的情况下使用这些块,以有组织的方式将语句序列分组。 然而,每个新 BEGIN...END 区块并不会创造新的词汇范围。

Transact-SQL 语法约定

Syntax

BEGIN [ ; ]
    { sql_statement | statement_block }
END [ ; ]

Arguments

{ sql_statementstatement_block}

使用语句块定义的任何有效的 Transact-SQL 语句或语句分组。

Remarks

一个 BEGIN...END 块必须包含至少一个语句。 如果你尝试使用空 BEGIN...END 块,即使你在每个关键词后加分号,也会导致语法错误。 你可以通过使用GOTO标签作为占位语句来避免BEGIN...END空块。 参见 示例C:使用GOTO标签表示动态生成的BEGIN...终结块

BEGIN...END 块可以嵌套。

BEGIN...END 区块并不定义任何词汇范围。 如果你在某个块内声明变量,它在整个父批处理中都能看到,而不仅仅是包含该 DECLARE 语句的块内。

你不能在多个批次中使用 BEGIN...END 区块。 例如,你不能在块内BEGIN...END使用GO批分隔器。

BEGIN...END 块来分组语句并不意味着组内的所有语句都以原子方式运行。 当批处理在事务外运行,且多语句 BEGIN...END 块的第二个语句引发错误或抛出异常时,第一个语句不会被回滚。

和 关键字后的BEGIN分号为可选,但建议使用,除非以下情况:END

  • 你需要在开头的关键词前 WITH 加分号,作为 常用表表达式 (CTE)。

  • 你需要一个分号,并且在一个区块内有语 THROW 句。

  • 后面用分号 BEGIN 避免与 BEGIN TRANSACTION “或 BEGIN ATOMIC ”语句混淆“。

  • 使用分号后 END 可确保后续任何语句,尤其是 WITH 关键词或 THROW 语句,都不需要前置分号。

虽然所有 Transact-SQL 语句在一个 BEGIN...END 区块内都是有效的,但你不应该将某些 Transact-SQL 语句集中在同一批次或语句块中。 确保对账单不与现有 Transact-SQL 批次要求冲突。

Examples

本文中的代码示例使用 AdventureWorks2025AdventureWorksDW2025 示例数据库,可以从 Microsoft SQL Server 示例和社区项目 主页下载该数据库。

在下例中, BEGIN 定义 END 逻辑相关的 Transact-SQL 语句序列按顺序执行。 示例还展示了嵌套的块。

USE AdventureWorks2025;
GO

DECLARE @personId AS INT = (
    SELECT p.BusinessEntityID
    FROM Person.Person AS p
    WHERE p.rowguid = { GUID '92C4279F-1207-48A3-8448-4636514EB7E2' }
);

IF (@personId IS NULL)
    THROW 50001, 'Person not found.', 1;

/* Concatenate the person's name fields: */;
BEGIN
    DECLARE @title AS NVARCHAR (8),
            @first AS NVARCHAR (50),
            @middle AS NVARCHAR (50),
            @last AS NVARCHAR (50),
            @suffix AS NVARCHAR (10);

    SELECT @title = NULLIF (p.Title, N''),
           @first = p.FirstName,
           @middle = NULLIF (p.MiddleName, N''),
           @last = p.LastName,
           @suffix = NULLIF (p.Suffix, N'')
    FROM Person.Person AS p
    WHERE p.BusinessEntityID = @personId;

    DECLARE @nameConcat AS NVARCHAR (255) = CONCAT_WS(N' ', @title, @first, @middle, @last, @suffix);

    /* This is a nested BEGIN...END block: */;
    BEGIN
        DECLARE @emails AS NVARCHAR (MAX) = (
            SELECT STRING_AGG(e.EmailAddress, /*separator:*/N'; ')
            FROM Person.EmailAddress AS e
            WHERE e.BusinessEntityID = @personId
        );

        SET @nameConcat = CONCAT(@nameConcat, N' (', @emails, N')');
    END
END

/* BEGIN...END blocks do not define a lexical scope, so
   even though @nameAndEmails is declared above, it is
   still in-scope after the END keyword. */
SELECT @nameConcat AS NameAndEmails;

B. 使用BEGIN...交易中的 END

在下面的示例中,BEGINEND 定义一系列一起执行的 Transact-SQL 语句。 如果没有包含该块, BEGIN...END 两个 ROLLBACK TRANSACTION 语句都会执行,并且两个 PRINT 消息都会返回。

USE AdventureWorks2025;
GO

BEGIN TRANSACTION;

IF @@TRANCOUNT = 0
    BEGIN
        SELECT FirstName,
               MiddleName
        FROM Person.Person
        WHERE LastName = 'Adams';

        ROLLBACK TRANSACTION;

        PRINT N'Rolling back the transaction two times causes an error.';
    END

ROLLBACK TRANSACTION;

PRINT N'Rolled back the transaction.';

C. 使用GOTO标签表示动态生成的BEGIN...端块

如果你用一个块生成动态 Transact-SQL BEGIN...END ,并且希望程序始终渲染关键 BEGIN...END 词,可以用 GOTO 标签作为占位语句,避免块是 BEGIN...END 空的。

BEGIN
    unusedLabel:
END

示例:Azure Synapse Analytics 和 Analytics Platform System (PDW)

C. 定义一系列连贯的语句

在下面的示例中,BEGINEND 定义一系列一起运行的 SQL 语句。

注意

如果去除 BEGINEND 关键字,下面的示例会进入无限循环。 该 WHILE 语句只循环查询, SELECT 且从未到达 SET @Iteration += 1 该语句。

-- Uses AdventureWorksDW;
DECLARE @Iteration AS INT = 0;

WHILE @Iteration < 10
    BEGIN
        SELECT FirstName,
               MiddleName
        FROM dbo.DimCustomer
        WHERE LastName = 'Adams';
        SET @Iteration + = 1;
    END