Подтвердить что ты не робот

Когда я не должен использовать точку с запятой?

Или: что не является выражением 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 и доступна ли она публично?

В противном случае, просто я верю, что говорит Ицик?

4b9b3361

Ответ 1

Синтаксис T-SQL не требует, чтобы точка с запятой завершала оператор.

На самом деле это устарел 1. Я точно не помню, но я думаю, что вам все равно не удастся использовать их на предстоящем Sql Server 2012, но в некоторых версиях после этого, вероятно, потребуется полуколония для каждого оператора. Использование полуколонны также технически требуется с помощью ansi standard. Дело в том, что настало время привыкнуть к использованию для каждого утверждения.

Как практический вопрос, я не ожидаю, что они последуют этому напрямую. Скорее, я ожидаю, что Sql Server Management Studio и другие средства разработки начнут выдавать предупреждения вместо ошибок, возможно, для нескольких версий. Это поможет разработчикам найти и исправить все старые несоответствующие коды. Но это не уменьшает сообщение: появляются полуколоны и скоро.

Для простой эвристики, когда не использовать полуточку, подумайте о коде, как если бы это был процедурный язык, который использовал фигурные скобки для блоков, например C/С++. Выражения, которые будут соединены с открытой (не закрывающей) фигурной скобкой, если она написана на языке процедуры, не должна иметь полуточку.

1 Это почти все в нижней части страницы

Ответ 2

Сводка, основанная на исходном каталоге OP, цитируемый список.

Да с запятой:

  • BEGIN TRAN;

Нет точки с запятой:

  • НАЧАТЬ
  • 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?