Когда не следует использовать точку с запятой? - PullRequest
47 голосов
/ 04 ноября 2011

Или: Что не является оператором T-SQL?

За исключением устранения неоднозначности, синтаксис T-SQL не требует точки с запятой для завершения оператора.Несмотря на это, Ицик Бен-Ган рекомендует использовать точку с запятой для завершения оператора 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.
*/

Ицик Бен-Ган противоречит этому вПример кода Excercise 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, Ицик отмечает некоторые исключения, которые не должны быть прекращены:

До сих пор требовалось использовать точку с запятой только в определенных случаях.Похоже, теперь планируется сделать его обязательным ограничителем для всех операторов * T-SQL в какой-либо будущей версии SQL Server.

(*) Естественно, есть случаи, которые не должны заканчиваться точкой с запятой;К ним относятся (но не ограничиваются ими):

  • BEGIN

  • BEGIN TRAN

  • IF

  • Иначе

  • WHILE

  • BEGIN TRY

  • END TRY

  • BEGIN CATCH

Кажется, что Ицик согласен с самим собой, но сама Microsoft не выполняет его рекомендации.Сравните Microsoft BEGIN TRANSACTION; и Ицика BEGIN TRAN в предыдущих примерах.

В поддерживаемом мною коде я видел даже ключевое слово BEGIN, оканчивающееся точкой с запятой:

IF @HasWidget = 0x1
BEGIN;
  SELECT WidgetID
  FROM tbWidgets;
END;

Iпредположим, что анализатор T-SQL может использовать точку с запятой после ключевого слова BEGIN для завершения пустого оператора, а не для завершения самого ключевого слова BEGIN;Я не верю, что BEGIN сам по себе является допустимым оператором T-SQL.

Эта гипотеза подтверждается тем фактом, что SQL Server 2008 успешно анализирует и выполняет следующий запрос:

SELECT 0;;

Это так запутанно, потому что нет общедоступной спецификации языка T-SQL, такой как Спецификация языка Java для Java, поэтому нигде нет формального определения оператора T-SQL.

Я не прав?Существует ли такая спецификация для T-SQL и является ли она общедоступной?

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

Ответы [ 3 ]

25 голосов
/ 04 ноября 2011

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

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

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

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

1 Это почти полностью ввнизу страницы

15 голосов
/ 04 апреля 2015

Сводка, основанная на оригинальном, цитируемом списке ОП.

Да точка с запятой:

  • BEGIN TRAN;

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

  • НАЧИНАЕТСЯ
  • ЕСЛИ
  • ИЛИ
  • НА *
  • НАЧИНАЕТСЯ ПОПРОБОВАТЬ
  • КОНЕЦ ПОПЫТКИ
  • 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

Тем не менее, их пример показывает точку с запятой после 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;

Вышеуказанное приводит к этой ошибке:

Сообщение 319, уровень 15, состояние 1, строка 13Неверный синтаксис рядом с ключевым словом «с».Если этот оператор является общим табличным выражением или предложением xmlnamespaces, предыдущий оператор должен заканчиваться точкой с запятой.

Он также выдает эту ошибку в SQL Server 2008 R2.

Itстановится еще более запутанным.Документация 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 вышеприведенный пакет генерирует эту ошибку:

Сообщение 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 12/29/2009 в 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

Однако, что 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 точка с запятой не так глупа.

5 голосов
/ 04 ноября 2011

Единственная ситуация, в которой я часто использую точку с запятой, - это использование общих табличных выражений через ключевое слово WITH - и только тогда, потому что ключевому слову WITH должна предшествовать точка с запятой, в противном случае оно возвращает ошибка. В этих случаях я пишу

;WITH [exp]...

т.е. Я ставлю перед точкой WITH точку с запятой, а не завершаю предыдущее утверждение.

Использование точки с запятой в SQL кажется очень редким; Я иногда вижу его после объявления хранимой процедуры или функции, что является скорее исключением, чем правилом. Из всех разработчиков, с которыми я работал, я не думаю, что кто-то действительно использовал точку с запятой так, как вы описали.

Заявления типа

BEGIN;
    SELECT WidgetID
    FROM tbWidgets;    
END;  

трудно понять - если BEGIN; считается оператором, независимым от его соответствующего END;, почему SELECT WidgetID не является допустимым оператором, независимым от его соответствующего FROM?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...