或者:什么不是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 它几乎一直在页面底部
Ril*_*jor 16
摘要,基于OP的原始引用列表.
是分号:
没有分号:
另外,在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
,分号并不是那么愚蠢.
归档时间: |
|
查看次数: |
19928 次 |
最近记录: |