2011-11-04 83 views
42

Hoặc: Điều gì không phải là câu lệnh T-SQL?Khi nào tôi không nên sử dụng dấu chấm phẩy?

Ngoại trừ để giải quyết sự mơ hồ, cú pháp T-SQL không yêu cầu dấu chấm phẩy để chấm dứt câu lệnh. Mặc dù vậy, Itzik Ben-Gan khuyên bạn nên sử dụng dấu chấm phẩy để chấm dứt câu lệnh T-SQL vì nó làm cho mã sạch hơn, dễ đọc hơn, dễ bảo trì hơn và dễ di chuyển hơn.

Tôi không biết định nghĩa chính xác về câu lệnh T-SQL hợp lệ là gì, vì vậy tôi có thể bị nhầm lẫn ở đây. Nhưng theo như tôi biết, BEGIN ... khối END là một câu lệnh T-SQL, vì vậy nên được chấm dứt bằng dấu chấm phẩy. Ví dụ:

IF OBJECT_ID('tempdb.dbo.#TempTable') IS NOT NULL 
BEGIN 
    DROP TABLE #TempTable; 
END; 

Đoạn mã ví dụ trong Microsoft BEGIN...END documentation hỗ trợ giả thuyết này:

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 mâu thuẫn này trong đoạn mã nguồn của tập thể dục 1-1 của T-SQL Fundamentals:

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; 

Tài liệu Transact-SQL Syntax Conventions của Microsoft tuyên bố rằng dấu chấm phẩy "sẽ được yêu cầu trong phiên bản tương lai" của T-SQL.

Bình luận về ý định của Microsoft để yêu cầu chấm phẩy trong một phiên bản tương lai của T-SQL, Itzik ghi nhận một số trường hợp ngoại lệ mà không phải là nghĩa vụ phải được chấm dứt:

So far it was a requirement to use a semicolon only in specific cases. Now it looks like the plan is to make it a required terminator for all* T-SQL statements in some future version of SQL Server.

(*) Naturally there are cases that aren’t supposed to be terminated with a semicolon; those include (but are not limited to):

  • BEGIN

  • BEGIN TRAN

  • IF

  • ELSE

  • WHILE

  • BEGIN TRY

  • END TRY

  • BEGIN CATCH

Itzik có vẻ là phù hợp với bản thân mình, nhưng Bản thân Microsoft không tuân theo các khuyến nghị của anh ta. So sánh các số BEGIN TRANSACTION; và Itzik của 01ztrong các ví dụ trước.

Trong đoạn code tôi duy trì, tôi đã thấy ngay cả những BEGIN từ khóa chấm dứt bởi dấu chấm phẩy:

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

Tôi tin rằng một phân tích cú pháp của T-SQL có thể xem xét các dấu chấm phẩy sau từ khóa BEGIN để chấm dứt một tuyên bố trống rỗng hơn chấm dứt chính từ khóa BEGIN; Tôi không tin rằng bản thân số BEGIN là một câu lệnh T-SQL hợp lệ.

phỏng đoán này được hỗ trợ bởi thực tế là SQL Server 2008 phân tích thành công và thực hiện các truy vấn sau đây:

SELECT 0;; 

Thật khó hiểu vì không có đặc điểm kỹ thuật phổ biến rộng rãi của ngôn ngữ T-SQL, như Java Language Specification cho Java, vì vậy không nơi nào có một định nghĩa chính thức của một câu lệnh T-SQL.

Tôi có sai không? Liệu một đặc điểm kỹ thuật như vậy tồn tại cho T-SQL, và nó có sẵn công khai không?

Nếu không, tôi có nên tin những gì Itzik nói không?

+0

Tôi vừa xem qua câu hỏi này đang tìm cách làm rõ về một trong những điểm chính xác của bạn - cho dù dấu chấm phẩy nên chấm dứt BEGIN và BEGIN TRAN - nhưng có vẻ như vẫn chưa có thẩm quyền rõ ràng về vấn đề này! –

+2

@AlastairAitchison Nếu bạn vẫn quan tâm, tôi đã yêu cầu Itzik giải thích lý do tại sao anh ấy nghĩ BEGIN TRAN không nên bị chấm dứt bởi dấu chấm phẩy trong một [bình luận về bài viết của anh ấy] (http://www.sqlmag.com/content2/topic/ semicolon-140706/catpath/tsql/seriespath/bối rối-by-t-sql-blog-15 # commentsAnchor). –

+0

Bạn cũng nên đặt câu hỏi rằng 'IF' xuất hiện trong danh sách. Chỉ vì không có dấu chấm phẩy nào xuất hiện ở giữa câu lệnh 'IF' không có nghĩa là nó bị bỏ qua. Ví dụ: dấu chấm phẩy trong 'IF 1 = 1 SELECT 1;' vẫn là một phần của câu lệnh 'IF'… – binki

Trả lời

23

T-SQL syntax does not require a semicolon to terminate a statement.

Trên thực tế, đây là deprecated . Tôi không thể nhớ chắc chắn, nhưng tôi nghĩ rằng bạn vẫn có thể nhận được ngay với không sử dụng chúng trong Sql Server 2012 sắp tới, nhưng một số phiên bản sau đó có thể sẽ yêu cầu một dấu chấm phẩy cho mỗi tuyên bố.Sử dụng dấu chấm phẩy cũng được yêu cầu về mặt kỹ thuật theo số ansi standard. Vấn đề là bây giờ là lúc để có thói quen sử dụng một cho mỗi câu lệnh.

Là một vấn đề thực tế, tôi không mong đợi họ làm theo thông qua trực tiếp điều này. Thay vào đó, tôi hy vọng Sql Server Management Studio và các công cụ phát triển khác để lần đầu tiên bắt đầu phát hành cảnh báo thay vì lỗi, có lẽ đối với một số phiên bản. Điều này sẽ giúp các nhà phát triển tìm và sửa tất cả mã không tuân thủ cũ. Nhưng điều đó không làm giảm bớt thông điệp: các dấu chấm phẩy đang đến và sớm thôi.

Để xem xét đơn giản khi không để sử dụng dấu chấm phẩy, hãy nghĩ mã như ngôn ngữ thủ tục đã sử dụng dấu ngoặc nhọn cho các khối, như C/C++. Các câu lệnh sẽ được ghép nối với một dấu ngoặc nhọn mở (không đóng) nếu được viết bằng ngôn ngữ thủ tục không được có dấu chấm phẩy.

Đó là hầu hết các con đường ở dưới cùng của trang

+0

+1; Tôi đã không nhận ra rằng không kết thúc một tuyên bố với dấu chấm phẩy đã không được chấp nhận trong phiên bản SQL Server 2008 của T-SQL. –

+0

Và cảm ơn cho một liên kết đến bản sao của tiêu chuẩn ANSI SQL: 92. Trong phần 4.22 nó định nghĩa tất cả các kiểu câu lệnh SQL ANSI.Nhưng tôi vẫn không thể tìm thấy bất kỳ tài liệu chính thức nào xác định phương ngữ T-SQL của Microsoft. Các MSDN [Transact-SQL Reference] (http://msdn.microsoft.com/en-us/library/bb510741.aspx) là điều gần nhất tôi đã tìm thấy. –

+0

Tôi đã chấp nhận câu trả lời này vì nó đưa ra lời khuyên đơn giản và hữu ích: luôn chấm dứt bằng dấu chấm phẩy. Vẫn còn một câu hỏi: tại sao Itzik nói rằng BEGIN TRAN là một ngoại lệ đối với quy tắc này? Tôi đặt câu hỏi trực tiếp cho anh ta trong một [bình luận về bài viết của anh ấy] (http://www.sqlmag.com/content2/topic/semicolon-140706/catpath/tsql/seriespath/puzzled-by-t-sql-blog- 15 # commentsAnchor). –

5

Tình hình duy nhất mà tôi thường xuyên sử dụng một dấu chấm phẩy được khi sử dụng Common Table Expressions qua từ khóa WITH - và chỉ sau đó bởi vì từ khóa WITH phải được bắt đầu bằng một dấu chấm phẩy nếu không nó sẽ trả về một lỗi. Trong những trường hợp này, tôi viết

;WITH [exp]... 

ví dụ: Tôi đứng trước WITH bằng dấu chấm phẩy, chứ không phải là chấm dứt các tuyên bố trước đó.

Sử dụng dấu chấm phẩy trong SQL có vẻ rất hiếm; Tôi thỉnh thoảng nhìn thấy nó sau khi một thủ tục lưu trữ hoặc khai báo hàm do đó là ngoại lệ chứ không phải là quy tắc. Của tất cả các nhà phát triển tôi đã làm việc với tôi không tin rằng bất kỳ đã thực sự sử dụng dấu chấm phẩy trong cách mà bạn mô tả.

cáo như

BEGIN; 
    SELECT WidgetID 
    FROM tbWidgets;  
END; 

là khó hiểu - nếu BEGIN; được coi là một tuyên bố độc lập của nó tương ứng END;, tại sao là SELECT WidgetID không phải là một tuyên bố có giá trị độc lập của nó tương ứng FROM?

+3

Câu lệnh trước một biểu thức bảng chung trong lô phải được chấm dứt bằng dấu chấm phẩy. Nếu biểu thức bảng chung bắt đầu lô, thì việc viết '; WITH' là không cần thiết. Trong mọi trường hợp, tôi nghĩ rằng điều này có vẻ xấu xí và không trực quan. –

+0

'SELECT x', giả sử' x' là một tên cột, không phải là một câu lệnh hợp lệ vì không có mệnh đề 'FROM' để định nghĩa một biểu thức bảng để chọn cột' x'. Nhưng câu lệnh 'SELECT' không yêu cầu mệnh đề' FROM' trong trường hợp chung: xem xét 'SELECT 0;' và 'DECLARE @x INT = 0; SELECT @x; '. Trong ví dụ thứ hai, biến vô hướng '@ x' có thể được chọn vì nó được định nghĩa trước đó. –

+0

@IainElder điểm của tôi đã được giải thích kém và tôi đã cập nhật; gợi ý là nếu một cặp từ khóa 'BEGIN/END' có thể được phân cách bằng dấu chấm phẩy thì tại sao không thể tách biệt các từ khóa' SELECT/FROM'? Tôi hiểu sự khác biệt nhưng tôi không thích dấu chấm phẩy sau khi một 'BEGIN' - nó có ý nghĩa với tôi. –

13

Tóm tắt, dựa trên danh sách được trích dẫn ban đầu của OP.

Có dấu chấm phẩy:

  • BEGIN TRAN;

Không dấu chấm phẩy:

  • BEGIN
  • NẾU
  • ELSE
  • KHI
  • BEGIN TRY
  • END TRY
  • BEGIN CATCH

Ngoài ra, hãy sử dụng chúng sau ENDEND CATCH.

chi tiết:

BEGIN TRAN là một tuyên bố và cần được chấm dứt với một dấu chấm phẩy.

tài liệu của Microsoft lưu ý tùy chọn dấu chấm phẩy:

BEGIN { TRAN | TRANSACTION } 
    [ { transaction_name | @tran_name_variable } 
     [ WITH MARK [ 'description' ] ] 
    ] 
[ ; ] 

dụ của Microsoft có dấu chấm phẩy:

BEGIN TRAN T1; 
UPDATE table1 ...; 
BEGIN TRAN M2 WITH MARK; 
UPDATE table2 ...; 
SELECT * from table1; 
COMMIT TRAN M2; 
UPDATE table3 ...; 
COMMIT TRAN T1; 

Cả hai ở trên là từ:

https://msdn.microsoft.com/en-us/library/ms188929(v=sql.90).aspx

Chúng khớp với tài liệu hiện tại:

https://msdn.microsoft.com/en-us/library/ms188929(v=sql.120).aspx

Đối với BEGIN...END, các tài liệu Microsoft không cung cấp hướng dẫn rõ ràng.

Định nghĩa không có dấu chấm phẩy:

BEGIN 
    { 
    sql_statement | statement_block 
    } 
END 

Tuy nhiên, tấm gương của họ cho thấy một dấu chấm phẩy sau 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

Đó trailing dấu chấm phẩy không phải là phù hợp với tài liệu riêng của Microsoft về kiểm soát IF cấu trúc ngôn ngữ luồng:

IF Boolean_expression 
    { sql_statement | statement_block } 
[ ELSE 
    { sql_statement | statement_block } ] 

Cả rằng định nghĩa cũng không dụ mã của họ cho thấy bất kỳ dấu chấm phẩy:

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

Tuy nhiên, tài liệu ELSE của họ, đồng thời cũng không hiển thị bất kỳ dấu chấm phẩy trong định nghĩa, làm chương trình một trong ví dụ, sau END cuối cùng.

Định nghĩa:

IF Boolean_expression { sql_statement | statement_block } 
    [ ELSE { sql_statement | statement_block } ] 

Ví dụ:

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

Tiêu chuẩn ANSI không giải quyết được sự nhập nhằng vì đây là những phần mở rộng không chuẩn:

Control-of-flow statements are not covered by the ANSI SQL standard because these are proprietary SQL extensions. The SQL Server Books Online is sketchy on the subject and many of the examples (as of this writing) are inconsistent and do not always include statement terminators. Furthermore, control-of-flow statement blocks are confusing due to the many variations, nesting, and optional BEGIN/END specifications.

http://www.dbdelta.com/always-use-semicolon-statement-terminators/

Tuy nhiên, hành vi của máy chủ làm sáng tỏ.Sau đây không phải là lỗi cú pháp trong SQL Server 2005:

DECLARE @foo int; 
IF @foo IS NULL 
BEGIN 
    WITH Blah AS 
    (
     SELECT 
      'a' AS a 
    ) 
    SELECT 
     a 
    FROM  Blah; 
END 

Vì vậy, bản thân số BEGIN không yêu cầu dấu chấm phẩy. Tuy nhiên, sau đây không tạo ra một lỗi cú pháp trong 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; 

Kết quả trên do lỗi này:

Msg 319, Level 15, State 1, Line 13 Incorrect syntax near the keyword 'with'. If this statement is a common table expression or an xmlnamespaces clause, the previous statement must be terminated with a semicolon.

Nó cũng ném rằng lỗi trong SQL Server 2008 R2.

Điều này càng trở nên khó hiểu hơn. Tài liệu của Microsoft cho TRY...CATCH hiển thị một dấu chấm phẩy tùy chọn sau END CATCH và các ví dụ của chúng phù hợp với điều đó.

BEGIN TRY 
    { sql_statement | statement_block } 
END TRY 
BEGIN CATCH 
    [ { sql_statement | statement_block } ] 
END CATCH 
[ ; ] 

Tuy nhiên, nếu bạn có CTE ngay sau khi BEGIN TRY, không có dấu chấm phẩy, nó sẽ phát sinh lỗi.

BEGIN TRY 
    WITH Blah AS 
    (
     SELECT 
      'a' AS a 
    ) 
    SELECT 
     a 
    FROM  Blah; 
END TRY 
BEGIN CATCH 
END CATCH 

Trong SQL Server 2008 R2, lô trên ném lỗi này:

Msg 319, Level 15, State 1, Line 2 Incorrect syntax near the keyword 'with'. If this statement is a common table expression, an xmlnamespaces clause or a change tracking context clause, the previous statement must be terminated with a semicolon.

Lỗi ngụ ý rằng BEGIN TRY là một tuyên bố (mà nó không phải là), và đó là một dấu chấm phẩy " khắc phục "vấn đề (mà nó làm). Đúng vậy, công trình này:

BEGIN TRY; 
    WITH Blah AS 
    (
     SELECT 
      'a' AS a 
    ) 
    SELECT 
     a 
    FROM  Blah; 
END TRY 
BEGIN CATCH 
END CATCH 

Tuy nhiên, Microsoft cho biết đó là thói quen không tốt:

Posted by Microsoft on 12/29/2009 at 12:11 PM I am resolving the corresonding SQL11 bug as "by design". Here is the explanation:

The semicolon between END TRY and BEGIN CATCH should not be allowed, because they are actually not different statements, but parts of the same TRY-CATCH statement. We only allow semicolons when they separate two statements in a sequence.

A word of explanation why then we allow semicolons after BEGIN TRY and BEGIN CATCH. These keywords serve as opening "parentheses" that start an embedded statement sequence. Semicolons after BEGIN TRY/BEGIN CATCH get parsed as part of that embedded sequence, with the first statement in the sequence being empty. While we allow this syntax, I would not recommend it as a good coding practice because it creates a wrong impression of BEGIN TRY/BEGIN CATCH being independent, standalone statements.

Cách đề nghị để xử lý tình huống đó là với thêm một BEGIN...END cho rõ ràng:

BEGIN TRY 
    BEGIN 
     WITH Blah AS 
     (
      SELECT 
       'a' AS a 
     ) 
     SELECT 
      a 
     FROM  Blah; 
    END 
END TRY 
BEGIN CATCH 
END CATCH 

Tuy nhiên, rằng END trước END TRY có lẽ nên có dấu chấm phẩy. Sau khi tất cả, điều này sẽ ném ra một lỗi:

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 

lẽ luôn trước một CTE WITH một dấu chấm phẩy không phải là quá ngớ ngẩn.

+1

Hai năm sau, nhưng tôi nghĩ tôi muốn chỉ ra rằng BEGIN được cho là có dấu chấm phẩy. Nếu bạn kiểm tra các ý kiến ​​trong liên kết này, bạn sẽ thấy rằng SSMS 17 bây giờ tạo ra một lỗi nếu bạn có một câu lệnh THROW sau khi một BEGIN không có dấu chấm phẩy. https://docs.microsoft.com/en-us/sql/t-sql/language-elements/throw-transact-sql – AndyJ

Các vấn đề liên quan