我何时不应该使用分号?

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

除了解决歧义外,T-SQL语法不需要用分号来终止语句。 尽pipe如此, Itzik Ben-Ganbuild议使用分号来终止T-SQL语句,因为它使代码变得更清晰,更具可读性,更易于维护和更便于携带。

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

IF OBJECT_ID('tempdb.dbo.#TempTable') IS NOT NULL BEGIN DROP TABLE #TempTable; END; 

微软的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. */ 

Itzik Ben-Gan在T-SQL基础的Excercise 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; 

Microsoft的“ Transact-SQL语法约定”文档指出T-SQL的分号“将来的版本”是必需的。

Itzik在评论微软打算在未来版本的T-SQL中要求分号的时候,注意到一些不应该被终止的例外:

到目前为止,仅在特定情况下才需要使用分号。 现在看起来计划是在将来的某个版本的SQL Server中将其作为所有* T-SQL语句的必需的终止符。

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

  • 开始

  • BEGIN TRAN

  • 如果

  • 其他

  • 开始尝试

  • 结束尝试

  • BEGIN CATCH

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

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

 IF @HasWidget = 0x1 BEGIN; SELECT WidgetID FROM tbWidgets; END; 

我相信一个T-SQLparsing器可能会考虑BEGIN关键字之后的分号来终止一个空语句,而不是终止BEGIN关键字本身; 我不相信BEGIN本身是一个有效的T-SQL语句。

SQL Server 2008成功parsing并执行以下查询的事实支持此推测:

 SELECT 0;; 

这很混乱,因为没有广泛的T-SQL语言规范 ,比如Java的Java语言规范 ,所以没有任何一个T-SQL语句的正式定义。

我错了吗? T-SQL是否有这样的规范,并且是公开的?

否则,只要我相信Itzik说什么?

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

其实这个已经过时了 1 。 我不记得当然,但是我认为你仍然可以在即将到来的Sql Server 2012中不使用它们,但是之后的某个版本可能需要每个语句都使用分号。 ANSI标准在技​​术上也需要使用分号。 重要的是,现在是养成每个陈述使用一个习惯的时候了。

作为一个实际的问题,我不指望他们直接跟随这一点。 相反,我期望Sql Server Management Studio和其他开发工具首先开始发布警告而不是错误,也许对于多个版本。 这将帮助开发人员查找并修复所有旧的不符合规范的代码。 但是这并不能减轻这个信息:分号码即将到来,很快就会出现。

对于何时使用分号的简单启发式,可以将代码看作是使用大括号(例如C / C ++)的程序语言。 如果用过程语言书写,那么与开头(不是结尾)大括号配对的语句不应该得到分号。

1 它几乎在页面的底部

总结,基于OP的原始引用列表。

是分号:

  • BEGIN TRAN;

没有分号:

  • 开始
  • 如果
  • 其他
  • 开始尝试
  • 结束尝试
  • BEGIN CATCH

另外,在ENDEND CATCH之后使用它们。

细节:

BEGIN TRAN是一个声明,应该用分号结尾。

Microsoft的文档logging了可选的分号:

 BEGIN { TRAN | TRANSACTION } [ { transaction_name | @tran_name_variable } [ WITH MARK [ 'description' ] ] ] [ ; ] 

微软的例子有分号:

 BEGIN TRAN T1; UPDATE table1 ...; BEGIN TRAN M2 WITH MARK; UPDATE table2 ...; SELECT * from table1; COMMIT TRAN M2; UPDATE table3 ...; COMMIT TRAN T1; 

以上两者均来自:

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 

但是,他们的示例在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; 

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

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

 IF Boolean_expression { sql_statement | statement_block } [ ELSE { sql_statement | statement_block } ] 

该定义和代码示例都没有显示任何分号:

 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)))+'.' 

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 } ] 

例:

 IF 1 = 1 PRINT 'Boolean_expression is true.' ELSE PRINT 'Boolean_expression is false.' ; 

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

ANSI标准不解决歧义,因为这些是非标准的扩展:

stream控制语句不在ANSI SQL标准中,因为这些是专有的SQL扩展。 SQL Server联机丛书在这个主题上是粗略的,许多例子(截至撰写本文)都不一致,并不总是包含语句终结符。 此外,stream控制语句块由于许多变化,嵌套和可选的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 

所以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; 

上面的结果是这个错误:

Msg 319,Level 15,State 1,Line 13关键字'with'附近的语法错误。 如果此语句是公用表expression式或xmlnamespaces子句,则前面的语句必须以分号结尾。

它也会在SQL Server 2008 R2中引发这个错误。

它变得更混乱。 Microsoft的TRY...CATCH文档在END CATCH之后显示了一个可选的分号,它们的示例与此相符。

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

但是,如果您在BEGIN TRY之后立即使用CTE,而没有使用分号,则会引发错误。

 BEGIN TRY WITH Blah AS ( SELECT 'a' AS a ) SELECT a FROM Blah; END TRY BEGIN CATCH END CATCH 

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

消息319,级别15,状态1,行2关键字“with”附近的语法错误。 如果此语句是公用表expression式,xmlnamespaces子句或变更跟踪上下文子句,则前面的语句必须以分号结尾。

这个错误意味着BEGIN TRY是一个声明(事实并非如此),而分号“修复”了这个问题。 没错,这是有效的:

 BEGIN TRY; WITH Blah AS ( SELECT 'a' AS a ) SELECT a FROM Blah; END TRY BEGIN CATCH END CATCH 

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

由微软发布于2009年12月29日在12:11下午我正在解决相应的SQL11错误为“按devise”。 这里是解释:

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

为什么然后我们在BEGIN TRY和BEGIN CATCH之后放一个分号呢? 这些关键字用作启动embedded式语句序列的开头“括号”。 BEGIN TRY / BEGIN CATCH之后的分号将作为该embedded序列的一部分进行分析,序列中的第一个语句为空。 虽然我们允许使用这种语法,但我不会将其推荐为一种很好的编码习惯,因为它会让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 

但是, END TRY之前的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 

也许总是在一个CTE之前用分号不是那么愚蠢。

在我经常使用分号的唯一情况是通过WITH关键字使用公用表expression式时 – 只有这样,因为WITH关键字必须以分号开头,否则返回一个错误。 在这些情况下,我写

 ;WITH [exp]... 

即我用分号前面加上WITH ,而不是终止前面的语句。

SQL中分号的使用似乎非常less见; 我经常在存储过程或函数声明之后偶尔看到它是exception而不是规则。 在我所使用的所有开发人员中,我不认为有任何人按照你所描述的方式使用了分号。

 BEGIN; SELECT WidgetID FROM tbWidgets; END; 

很难理解 – 如果BEGIN; 被认为是独立于其相应END;的声明END; 为什么SELECT WidgetID不是一个独立于其相应FROM的有效语句?