我何时不应该使用分号?
或者: 什么不是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
另外,在END
和END 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
的有效语句?