我什么时候不应该使用分号?

Iai*_*der 48 t-sql

或者:什么不是T-SQL语句?

除了解决歧义之外,T-SQL语法不需要分号来终止语句.尽管如此,Itzik Ben-Gan建议使用分号来终止T-SQL语句,因为它使代码更清晰,更易读,更易于维护,更便于携带.

我不知道有效的T-SQL语句是什么的精确定义,所以我可能会在这里感到困惑.但据我所知,BEGIN ... END块是一个T-SQL语句,因此应以分号结束.例如:

IF OBJECT_ID('tempdb.dbo.#TempTable') IS NOT NULL
BEGIN
  DROP TABLE #TempTable;
END;
Run Code Online (Sandbox Code Playgroud)

Microsoft的BEGIN ... END文档中的代码示例支持此猜想:

USE AdventureWorks2008R2;
GO
BEGIN TRANSACTION;
GO
IF @@TRANCOUNT = 0
BEGIN
    SELECT FirstName, MiddleName 
    FROM Person.Person WHERE LastName = 'Adams';
    ROLLBACK TRANSACTION;
    PRINT N'Rolling back the transaction two times would cause an error.';
END;
ROLLBACK TRANSACTION;
PRINT N'Rolled back the transaction.';
GO
/*
Rolled back the tranaction.
*/
Run Code Online (Sandbox Code Playgroud)

Itzik Ben-Gan在T-SQL Fundamentals的练习1-1的代码示例中与此相矛盾:

SET NOCOUNT ON;
USE TSQLFundamentals2008;
IF OBJECT_ID('dbo.Nums', 'U') IS NOT NULL DROP TABLE dbo.Nums;
CREATE TABLE dbo.Nums(n INT NOT NULL PRIMARY KEY);

DECLARE @i AS INT = 1;
BEGIN TRAN
  WHILE @i <= 100000
  BEGIN
    INSERT INTO dbo.Nums VALUES(@i);
    SET @i = @i + 1;
  END
COMMIT TRAN
SET NOCOUNT OFF;
Run Code Online (Sandbox Code Playgroud)

Microsoft的Transact-SQL语法约定文档指出在T-SQL的未来版本中将使用分号".

在评论微软打算在未来版本的T-SQL中使用分号时,Itzik指出了一些不应该被终止的异常:

到目前为止,仅在特定情况下才需要使用分号.现在看起来计划是使它成为SQL Server未来版本中所有*T-SQL语句的必需终结符.

(*)当然有些情况不应以分号结束; 那些包括(但不限于):

  • 开始

  • 开始了

  • 如果

  • 其他

  • 开始尝试

  • 结束

  • 开始捕捉

Itzik似乎与自己一致,但微软本身并不遵循他的建议.比较前面示例中的Microsoft BEGIN TRANSACTION;和Itzik BEGIN TRAN.

在我维护的代码中,我甚至看到BEGIN以分号结尾的关键字:

IF @HasWidget = 0x1
BEGIN;
  SELECT WidgetID
  FROM tbWidgets;
END;
Run Code Online (Sandbox Code Playgroud)

我相信T-SQL解析器可能会考虑在BEGIN关键字后面的分号来终止空语句而不是终止BEGIN关键字本身; 我不相信它BEGIN本身是一个有效的T-SQL语句.

SQL Server 2008成功解析并执行以下查询这一事实支持了这一猜想:

SELECT 0;;
Run Code Online (Sandbox Code Playgroud)

它是如此令人困惑,因为没有广泛可用的T-SQL语言规范,如Java语言规范 Java,因此没有任何形式的T-SQL语句的正式定义.

我错了吗?T-SQL是否存在此类规范,是否可公开获取?

否则,我应该相信Itzik说的话吗?

Joe*_*orn 26

T-SQL语法不需要分号来终止语句.

实际上,这已被弃用1.我不记得了,但我认为你仍然可以在即将推出的Sql Server 2012中使用它们,但是之后的某个版本可能需要为每个语句使用分号.ansi标准在技​​术上也需要使用分号.关键是现在是时候养成为每个陈述使用一个的习惯了.

实际上,我不希望他们直接遵循这一点.相反,我希望Sql Server Management Studio和其他开发工具首先开始发出警告而不是错误,可能是针对多个版本.这将有助于开发人员查找并修复所有旧的不合规代码.但这并没有减少这一信息:半冒险即将来临.

对于使用分号的简单启发式算法,可以将代码看作是一个过程语言,使用大括号来表示块,比如C/C++.如果用过程语言编写,将与开头(非结束)花括号括起来的语句不应该是分号.

1 它几乎一直在页面底部

  • 虽然良好的信息和链接,我不认为这真的回答了这个问题.OP希望在适当的地方使用分号,但想知道哪些陈述不应该使用它们.这个答案仅仅是轶事指导("思考......程序性"),而不是明确的清单. (3认同)

Ril*_*jor 16

摘要,基于OP的原始引用列表.

是分号:

  • BEGIN TRAN;

没有分号:

  • 开始
  • 如果
  • 其他
  • 开始尝试
  • 结束
  • 开始捕捉

另外,在END和之后使用它们END CATCH.

细节:

BEGIN TRAN 是一个声明,应以分号结束.

Microsoft的文档记录了可选的分号:

BEGIN { TRAN | TRANSACTION } 
    [ { transaction_name | @tran_name_variable }
      [ WITH MARK [ 'description' ] ]
    ]
[ ; ]
Run Code Online (Sandbox Code Playgroud)

微软的例子有分号:

BEGIN TRAN T1;
UPDATE table1 ...;
BEGIN TRAN M2 WITH MARK;
UPDATE table2 ...;
SELECT * from table1;
COMMIT TRAN M2;
UPDATE table3 ...;
COMMIT TRAN T1;
Run Code Online (Sandbox Code Playgroud)

以上都来自:

https://msdn.microsoft.com/en-us/library/ms188929(v=sql.90).aspx

它们与当前文档匹配:

https://msdn.microsoft.com/en-us/library/ms188929(v=sql.120).aspx

至于BEGIN...END,Microsoft文档没有提供明确的指导.

该定义没有分号:

BEGIN
     { 
    sql_statement | statement_block 
     } 
END
Run Code Online (Sandbox Code Playgroud)

但是,他们的示例在END之后显示了一个分号:

IF @@TRANCOUNT = 0
BEGIN
    SELECT FirstName, MiddleName 
    FROM Person.Person WHERE LastName = 'Adams';
    ROLLBACK TRANSACTION;
    PRINT N'Rolling back the transaction two times would cause an error.';
END;
Run Code Online (Sandbox Code Playgroud)

https://msdn.microsoft.com/en-us/library/ms190487.aspx

尾随的分号与微软自己IF的流语言构造控制文档不一致:

IF Boolean_expression 
     { sql_statement | statement_block } 
[ ELSE 
     { sql_statement | statement_block } ] 
Run Code Online (Sandbox Code Playgroud)

该定义及其代码示例均未显示任何分号:

DECLARE @compareprice money, @cost money 
EXECUTE Production.uspGetList '%Bikes%', 700, 
    @compareprice OUT, 
    @cost OUTPUT
IF @cost <= @compareprice 
BEGIN
    PRINT 'These products can be purchased for less than 
    $'+RTRIM(CAST(@compareprice AS varchar(20)))+'.'
END
ELSE
    PRINT 'The prices for all products in this category exceed 
    $'+ RTRIM(CAST(@compareprice AS varchar(20)))+'.'
Run Code Online (Sandbox Code Playgroud)

https://msdn.microsoft.com/en-us/library/ms182717(v=sql.110).aspx

但是,他们的ELSE文档虽然在定义中没有显示任何分号,但在最后一个示例中确实显示了一个END.

定义:

IF Boolean_expression { sql_statement | statement_block } 
    [ ELSE { sql_statement | statement_block } ] 
Run Code Online (Sandbox Code Playgroud)

例:

IF 1 = 1 PRINT 'Boolean_expression is true.'
ELSE PRINT 'Boolean_expression is false.' ;
Run Code Online (Sandbox Code Playgroud)

https://msdn.microsoft.com/en-us/library/ms182587(v=sql.110).aspx

ANSI标准不解决歧义,因为它们是非标准扩展:

ANSI SQL标准不涵盖控制流语句,因为它们是专有的SQL扩展.SQL Server联机丛书在这个主题上是粗略的,许多示例(在撰写本文时)是不一致的,并不总是包含语句终止符.此外,由于许多变化,嵌套和可选的BEGIN/END规范,控制流语句块令人困惑.

http://www.dbdelta.com/always-use-semicolon-statement-terminators/

但是,服务器的行为有所启发.以下不是SQL Server 2005中的语法错误:

DECLARE @foo int;
IF @foo IS NULL
BEGIN
    WITH Blah AS
    (
        SELECT
            'a' AS a
    )
    SELECT
        a
    FROM        Blah;
END
Run Code Online (Sandbox Code Playgroud)

所以它BEGIN本身不需要分号.但是,以下确实在SQL Server 2005中产生语法错误:

DECLARE @foo int;
IF @foo IS NULL
BEGIN
    WITH Blah AS
    (
        SELECT
            'a' AS a
    )
    SELECT
        a
    FROM        Blah;
END
WITH Blah2 AS
(
    SELECT
        'a' AS a
)
SELECT
    a
FROM        Blah2;
Run Code Online (Sandbox Code Playgroud)

以上结果导致此错误:

消息319,级别15,状态1,行13关键字'with'附近的语法不正确.如果此语句是公用表表达式或xmlnamespaces子句,则必须以分号结束前一个语句.

它还会在SQL Server 2008 R2中引发该错误.

它变得更加令人困惑.微软的文档TRY...CATCH显示了一个可选的分号END CATCH,并且它们的例子与之一致.

BEGIN TRY
     { sql_statement | statement_block }
END TRY
BEGIN CATCH
     [ { sql_statement | statement_block } ]
END CATCH
[ ; ]
Run Code Online (Sandbox Code Playgroud)

但是,如果您在a之后立即有一个CTE BEGIN TRY,没有分号,则会抛出错误.

BEGIN TRY
    WITH Blah AS
    (
        SELECT
            'a' AS a
    )
    SELECT
        a
    FROM        Blah;
END TRY
BEGIN CATCH
END CATCH
Run Code Online (Sandbox Code Playgroud)

在SQL Server 2008 R2中,上述批处理抛出此错误:

消息319,级别15,状态1,行2关键字'with'附近的语法不正确.如果此语句是公用表表达式,xmlnamespaces子句或更改跟踪上下文子句,则必须以分号终止先前的语句.

错误意味着这BEGIN TRY是一个声明(它不是),并且分号"修复"了问题(它确实如此).没错,这有效:

BEGIN TRY;
    WITH Blah AS
    (
        SELECT
            'a' AS a
    )
    SELECT
        a
    FROM        Blah;
END TRY
BEGIN CATCH
END CATCH
Run Code Online (Sandbox Code Playgroud)

但是,微软表示这不是一个好的做法:

微软于2009年12月29日下午12:11发布我正在解决相应的SQL11错误"按设计".这是解释:

不应允许END TRY和BEGIN CATCH之间的分号,因为它们实际上不是不同的语句,而是相同的TRY-CATCH语句的一部分.我们只在分隔序列中的两个语句时才允许使用分号.

一句解释为什么我们在BEGIN TRY和BEGIN CATCH之后允许分号.这些关键字用作开始嵌入语句序列的开头"括号".BEGIN TRY/BEGIN CATCH之后的分号被解析为嵌入序列的一部分,序列中的第一个语句为空.虽然我们允许这种语法,但我不建议将其作为一种良好的编码实践,因为它会产生错误的印象,即BEGIN TRY/BEGIN CATCH是独立的独立语句.

处理这种情况的推荐方法是BEGIN...END为了清楚起见:

BEGIN TRY
    BEGIN
        WITH Blah AS
        (
            SELECT
                'a' AS a
        )
        SELECT
            a
        FROM        Blah;
    END
END TRY
BEGIN CATCH
END CATCH
Run Code Online (Sandbox Code Playgroud)

但是,END之前END TRY应该有一个分号.毕竟,这会抛出一个错误:

BEGIN TRY
    BEGIN
        WITH Blah AS
        (
            SELECT
                'a' AS a
        )
        SELECT
            a
        FROM        Blah;
    END
    WITH Blah2 AS
    (
        SELECT
            'b' AS b
    )
    SELECT
        b
    FROM        Blah2;
END TRY
BEGIN CATCH
END CATCH
Run Code Online (Sandbox Code Playgroud)

也许总是在CTE之前WITH,分号并不是那么愚蠢.

  • 根据后来对该页面的评论(https://docs.microsoft.com/en-us/sql/t-sql/language-elements/throw-transact-sql?view=sql-server-2017),这似乎是 SSMS 中的错误,因此在 BEGIN 之后_不需要_需要分号。 (3认同)
  • 两年过去了,但我想指出一点,就是BEGIN应该有一个分号。如果您查看此链接中的注释,您会发现,如果在BEGIN之后没有分号的情况下有THROW语句,SSMS 17现在会产生错误。https://docs.microsoft.com/zh-cn/sql/t-sql/language-elements/throw-transact-sql (2认同)