Когда я не должен использовать точку с запятой?
Или: что не является выражением 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 документация поддерживает эту гипотезу:
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.
*/
Ицик Бен-Ган противоречит этому в примере кода Упражнения 1-1 Основы T-SQL:
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.
Комментируя намерение Microsoft потребовать точку с запятой в будущей версии T-SQL, Itzik отмечает некоторые исключения, которые не должны быть прерваны:
До сих пор требовалось использовать точку с запятой только в определенных случаях. Теперь похоже, что план должен сделать его обязательным терминатором для всех * операторов T-SQL в некоторой будущей версии SQL Server.
(*) Естественно, существуют случаи, когда arent должен заканчиваться точкой с запятой; они включают (но не ограничиваются ими):
-
НАЧАТЬ
-
BEGIN TRAN
-
IF
-
ELSE
-
WHILE
-
BEGIN TRY
-
END TRY
-
BEGIN CATCH
Ицик, похоже, согласен с самим собой, но сама Microsoft не следует его рекомендациям. Сравните 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 Language Specification для Java, поэтому нигде не существует формального определения инструкция T-SQL.
Неужели я ошибаюсь? Существует ли такая спецификация для T-SQL и доступна ли она публично?
В противном случае, просто я верю, что говорит Ицик?
Ответы
Ответ 1
Синтаксис T-SQL не требует, чтобы точка с запятой завершала оператор.
На самом деле это устарел 1. Я точно не помню, но я думаю, что вам все равно не удастся использовать их на предстоящем Sql Server 2012, но в некоторых версиях после этого, вероятно, потребуется полуколония для каждого оператора. Использование полуколонны также технически требуется с помощью ansi standard. Дело в том, что настало время привыкнуть к использованию для каждого утверждения.
Как практический вопрос, я не ожидаю, что они последуют этому напрямую. Скорее, я ожидаю, что Sql Server Management Studio и другие средства разработки начнут выдавать предупреждения вместо ошибок, возможно, для нескольких версий. Это поможет разработчикам найти и исправить все старые несоответствующие коды. Но это не уменьшает сообщение: появляются полуколоны и скоро.
Для простой эвристики, когда не использовать полуточку, подумайте о коде, как если бы это был процедурный язык, который использовал фигурные скобки для блоков, например C/С++. Выражения, которые будут соединены с открытой (не закрывающей) фигурной скобкой, если она написана на языке процедуры, не должна иметь полуточку.
1 Это почти все в нижней части страницы
Ответ 2
Сводка, основанная на исходном каталоге OP, цитируемый список.
Да с запятой:
Нет точки с запятой:
- НАЧАТЬ
- IF
- ELSE
- WHILE
- BEGIN TRY
- END TRY
- BEGIN CATCH
Кроме того, используйте их после END
и END CATCH
.
Подробнее:
BEGIN TRAN
является инструкцией и должен быть завершен с помощью двоеточия.
Документация Microsoft отмечает необязательную полуточку:
BEGIN { TRAN | TRANSACTION }
[ { transaction_name | @tran_name_variable }
[ WITH MARK [ 'description' ] ]
]
[ ; ]
Пример Microsoft имеет два столбца:
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
Однако их пример показывает после запятой точку с запятой:
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
Эта конечная точка с запятой не согласуется с собственной документацией Microsoft для 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;
Приведенная выше ошибка приводит к ошибке:
Msg 319, уровень 15, состояние 1, строка 13 Некорректный синтаксис рядом с ключевым словом 'с'. Если это утверждение является общим табличным выражением или 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
[ ; ]
Однако, если у вас есть CTE сразу после BEGIN TRY
, без точки с запятой, он выдает ошибку.
BEGIN TRY
WITH Blah AS
(
SELECT
'a' AS a
)
SELECT
a
FROM Blah;
END TRY
BEGIN CATCH
END CATCH
В SQL Server 2008 R2 вышеуказанная партия выдает эту ошибку:
Msg 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
Однако Microsoft говорит, что это не очень хорошая практика:
Отправлено Microsoft от 29.12.2009 в 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
полуточка не так глупа.
Ответ 3
Единственная ситуация, когда я часто использую точку с запятой, - это использовать Общие выражения таблицы с помощью ключевого слова WITH
- и только тогда, когда WITH
ключевому слову должно предшествовать точка с запятой, иначе она возвращает ошибку. В этих случаях я пишу
;WITH [exp]...
то есть. Я предшествую WITH
точкой с запятой, а не завершаю предыдущий оператор.
Использование точки с запятой в SQL представляется очень редким; Иногда я вижу это после объявления хранимой процедуры или функции, которое является исключением, а не правилом. Из всех разработчиков, с которыми я работал, я не верю, что кто-то действительно использовал точку с запятой так, как вы описали.
Заявления вроде
BEGIN;
SELECT WidgetID
FROM tbWidgets;
END;
трудно понять - если BEGIN;
считается оператором, независимым от его соответствующего END;
, почему SELECT WidgetID
не является допустимым оператором, независимым от его соответствующего FROM
?