或者:什么不是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;
Microsoft的BEGIN...END documentation中的代码示例支持这个猜想:
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 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;
Microsoft的Transact-SQL Syntax Conventions文档声明在T-SQL的未来版本中将需要分号。
在评论微软打算在未来版本的T-SQL中要求分号时,Itzik指出了一些不应该被终止的例外:
到目前为止,仅在特定情况下才需要使用分号。现在看起来计划是使它成为SQL Server未来版本中所有* T-SQL语句的必需终结符。
(*)当然有些情况不应以分号结尾;这些包括(但不限于):
BEGIN
BEGIN TRAN
如果
ELSE
WHILE
开始尝试
结束尝试
BEGIN CATCH
Itzik似乎与自己一致,但微软本身并不遵循他的建议。在前面的示例中比较Microsoft的BEGIN TRANSACTION;
和Itzik的BEGIN TRAN
。
在我维护的代码中,我甚至看到BEGIN
关键字以分号结尾:
IF @HasWidget = 0x1
BEGIN;
SELECT WidgetID
FROM tbWidgets;
END;
我相信T-SQL解析器可能会考虑BEGIN
关键字后面的分号来终止空语句,而不是终止BEGIN
关键字本身;我不相信BEGIN
本身就是一个有效的T-SQL语句。
SQL Server 2008成功解析并执行以下查询这一事实支持了这一猜想:
SELECT 0;;
这很混乱,因为没有广泛可用的T-SQL语言规范,比如Java的Java Language Specification,所以没有T-SQL语句的正式定义。
我错了吗? T-SQL是否存在这样的规范,是否公开发布?
否则,我应该相信Itzik所说的吗?
答案 0 :(得分:25)
T-SQL语法不需要分号来终止语句。
实际上,这是deprecated 1 。我不记得了,但我认为你仍然可以在即将推出的Sql Server 2012中使用它们,但是之后的某个版本可能需要为每个语句使用分号。 ansi standard在技术上也需要使用分号。关键是现在是时候养成为每个陈述使用一个的习惯。
实际上,我不希望他们直接遵循这一点。相反,我希望Sql Server Management Studio和其他开发工具首先开始发出警告而不是错误,可能是针对多个版本。这将有助于开发人员查找并修复所有旧的不合规代码。但这并没有减少这一信息:半冒险即将来临。
对于不使用分号时的简单启发式算法,请将代码视为使用大括号的过程语言,如C / C ++。如果用过程语言编写,将与开头(非结束)大括号配对的语句不应该是分号。
1 它几乎一直在页面底部
答案 1 :(得分:14)
摘要,基于OP的原始引用列表。
是分号:
没有分号:
另外,请在END
和END CATCH
之后使用它们。
详细说明:
BEGIN TRAN
是一个声明,应以分号结尾。
Microsoft的文档说明了可选的分号:
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
这个尾随的分号与微软自己的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标准不解决歧义,因为它们是非标准扩展:
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
因此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;
以上导致此错误:
消息319,级别15,状态1,行13关键字附近的语法不正确 “用”。如果此语句是公用表表达式或 xmlnamespaces子句,前一个语句必须以a结尾 分号。
它还会在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关键字附近的语法不正确 “用”。如果此语句是公用表表达式,则为 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错误视为“按设计”。以下是解释:
不应允许END TRY和BEGIN CATCH之间的分号, 因为它们实际上并不是不同的陈述,而是部分的 同样的TRY-CATCH声明。我们只在分开时才允许使用分号 序列中的两个陈述。
解释为什么我们在BEGIN TRY和之后允许使用分号 开始捕捉。这些关键字用作开头的“括号” 嵌入语句序列。 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
但是,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
也许总是在CTE WITH
之前,分号并不是那么愚蠢。
答案 2 :(得分:5)
我经常使用分号的唯一情况是通过WITH
关键字使用Common Table Expressions - 然后才会因为WITH
关键字前面必须加分号,否则返回一个错误。在那些情况下,我写了
;WITH [exp]...
即。我在WITH
之前加上分号,而不是终止前一个语句。
SQL中的分号用法似乎非常少见;我偶尔会在存储过程或函数声明之后看到它,这是异常而不是规则。在我与之合作的所有开发人员中,我不相信任何人真正以你所描述的方式使用分号。
之类的陈述
BEGIN;
SELECT WidgetID
FROM tbWidgets;
END;
很难理解 - 如果BEGIN;
被视为独立于其对应END;
的声明,为什么SELECT WidgetID
不是独立于其对应FROM
的有效声明?< / p>