SQL Server: Các câu lệnh theo lô (tức là sử dụng “GO”) tốt cho điều gì?


77

Tôi biết rằng trong SQL Server GO được coi là một bộ phân tách hàng loạt .

Câu hỏi của tôi là: Có ích gì khi có máy tách lô? Nó mang lại cho bạn lợi ích gì và tại sao bạn muốn sử dụng nó?

Ví dụ: Tôi thường thấy nó được sử dụng trong mã SQL như sau và tôi không thể hiểu tại sao nó được coi là phương pháp hay nhất. Theo như tôi có thể nói mã sẽ giống nhau mà không có tất cả các GOcâu lệnh:

USE AdventureWorks2012;
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

(nguồn: tài liệu technet ):



7
@JohnnyBones, không nó không giống nhau (tôi thậm chí đã liên kết với bài đăng đó trong câu hỏi của mình). Bài đó về cơ bản nói rằng GO là một dấu phân cách hàng loạt, nhưng câu hỏi của tôi là yêu cầu những gì một tách mẻ là tốt cho
Zain Rizvi

Nhìn vào câu trả lời được cung cấp bởi tvanfosson trong câu hỏi đó (26 lượt bình chọn).
Johnny Bones

3
@JohnnyBones cảm ơn vì đã chỉ ra câu trả lời đó. Nó cung cấp cho một số tốt thêm thông tin nền, nhưng không trả lời câu hỏi gốc Tôi đã (heck, một người thậm chí đã để lại một bình luận về câu hỏi đó hỏi những gì kịch bản trạm trộn là tốt cho)
Zain Rizvi

Chắc chắn là một bản sao của điều này ... stackoverflow.com/questions/2668529/t-sql-go-statement/…
sam yi

Câu trả lời:


41

Trong ví dụ, nó không có ích gì.

Tuy nhiên, nhiều câu lệnh phải là những câu duy nhất trong lô.

Chẳng hạn như CREATE PROCEDURE.

Cũng thường sau khi thực hiện các thay đổi lược đồ (ví dụ: thêm một cột mới vào bảng hiện có) các câu lệnh sử dụng lược đồ mới phải được biên dịch riêng biệt trong một lô khác.

Nói chung, một giải pháp thay thế cho việc gửi các lô riêng biệt được phân tách bằng GOlà thực thi SQL trong một lô con bằng cách sử dụngEXEC


1
Điều đó có ý nghĩa, cảm ơn. Tôi cũng đã thấy nó được sử dụng trong các tập lệnh nâng cấp lược đồ cơ sở dữ liệu sau mỗi thao tác thay đổi / tạo bảng ngay cả khi các thao tác tuần tự không phụ thuộc vào thao tác trước đó. Có bất kỳ lý do gì để làm điều đó, hoặc sẽ mà đã là một thực tế để không làm cho các nhà phát triển phải nghĩ đến khó khăn về SQL của họ
Zain Rizvi

1
@Zain - Nó không gây hại nhiều (ngoài việc gia tăng các chuyến đi vòng quanh mạng) và có thể tiết kiệm một số lỗi trong đó các hoạt động phụ thuộc vào các hoạt động trước đó.
Martin Smith

Có vẻ như mã SQL sẽ cần được chia thành hai lô khi lô thứ hai hoạt động trên các đối tượng được tạo bởi lô đầu tiên. Nói cách khác, khi lô thứ hai yêu cầu công cụ lập kế hoạch SQL sử dụng các tạo tác chưa tồn tại, điều này sẽ làm hỏng việc lập kế hoạch. Đúng không?
Zain Rizvi

7
@Zain - Đối với các bảng có liên quan, các thay đổi đối với các bảng hiện có có xu hướng gây ra nhiều vấn đề hơn. CREATE TABLE T(X INT);SELECT * FROM Thoạt động tốt vì SELECTnó có thể được biên dịch chậm lại. Nhưng sau đó bảng được tạo ALTER TABLE T ADD Y INT;SELECT Y FROM Tsẽ không thành công với lỗi tên cột không hợp lệ vì câu lệnh đó không được hoãn biên dịch.
Martin Smith

Đối với tôi điều này vẫn không trả lời câu hỏi. "Tuy nhiên, nhiều câu lệnh phải là những câu duy nhất trong lô" - nhưng lợi ích của việc có một loạt là gì? Nếu máy chủ có một số giới hạn nội bộ yêu cầu "nhiều câu lệnh" là những câu lệnh duy nhất trong một lô - intellisense có thể phát hiện ra yêu cầu một cách rõ ràng, vậy tại sao lại yêu cầu tôi viết GO? Tại sao không chỉ chạy nội bộ theo cách chúng cần được thực hiện? Để Martin - "biên dịch bị hoãn lại"? Tại sao một lập trình viên SQL cần hiểu các quy trình biên dịch nội bộ? Câu hỏi vẫn còn là: Điểm của lô là gì?
youcantryreachingme

30

Như TechNet nói , GOnó báo hiệu sự kết thúc của một lô SQL đối với các tiện ích SQL. Ví dụ: khi SQL Server Management Studio gặp phải dấu phân tách hàng loạt, nó biết tất cả văn bản cho đến nay là một truy vấn SQL độc lập.

Chúng tôi sử dụng một kỹ thuật tương tự trong phần mềm của mình. Chúng tôi giữ tất cả procs, tập lệnh lược đồ, chuyển đổi dữ liệu, v.v., trong các tệp tập lệnh SQL (đã đăng ký để kiểm soát nguồn). Khi trình cài đặt của chúng tôi đọc một trong những tệp kịch bản này, GO sẽ nói với trình phân tích cú pháp của chúng tôi "bạn có thể chạy SQL mà bạn đã đọc".

Tính năng thú vị về bộ phân tách hàng loạt GOlà bạn có thể gộp hai truy vấn SQL lại với nhau trong cùng một tập lệnh thường gây ra lỗi. Ví dụ: cố gắng thả và tạo lại cùng một quy trình được lưu trữ trong cùng một tệp kịch bản:

if exists (select * from sys.procedures where name = 'sp_test')
    drop procedure sp_test

create procedure sp_test as
begin
    select 1
end

Nếu bạn chạy mã trên, bạn sẽ gặp lỗi:

Msg 156, Mức 15, Trạng thái 1, Thủ tục sp_test, Dòng 5 Cú pháp không chính xác gần từ khóa 'begin'.

Và SSMS sẽ hiển thị cho bạn lỗi:

Cú pháp không chính xác. 'TẠO THỦ TỤC' phải là câu lệnh duy nhất trong một lô.

Sử dụng dấu phân tách hàng loạt có thể giúp bạn khắc phục lỗi này:

if exists (select * from sys.procedures where name = 'sp_test')
    drop procedure sp_test
GO
create procedure sp_test as
begin
    select 1
end

Điều này rất tiện dụng nếu bạn muốn một tập lệnh SQL duy nhất trong điều khiển nguồn để duy trì một thủ tục hoặc hàm được lưu trữ. Chúng tôi sử dụng mẫu này thường xuyên.

Một điều thú vị khác mà bạn có thể làm là sử dụng nó để chạy một truy vấn nhiều lần:

INSERT INTO MyTable (...) ...
GO 10 -- run all the above 10 times!

Như các câu trả lời cho câu hỏi SO này , bạn cũng có thể cấu hình nó theo bất kỳ thứ gì bạn muốn. Nếu bạn muốn gây rối với đồng nghiệp của mình, hãy đặt dấu phân tách hàng loạt thành "WHERE" thay vì "GO". Vui vẻ! :)


13
Thú vui kiểu đó có thể nguy hiểm đến sức khỏe của một người.
Dylan Brams

2
Ý bạn là GO gặp lỗi chỉ tồn tại vì GO tồn tại? Nếu Intellisense có thể phát hiện GO được yêu cầu, chắc chắn máy chủ cũng có thể xử lý vấn đề. Đối với tôi, không có gì sai về mặt logic với "nếu tồn tại, thả, và sau đó, tạo". Các tài liệu lưu ý rằng tất cả các câu lệnh trong lô GO được biên dịch thành một kế hoạch thực thi duy nhất - đây là lý do tại sao các câu lệnh đó sẽ không thành công, nhưng một lần nữa - nếu intellisense có thể phát hiện ra nó, chắc chắn máy chủ cũng có thể xử lý nó. go 10thực tế là hữu ích - nhưng không bắt buộc theo cách gonày được yêu cầu trong ví dụ trước đó của bạn.
youcantryreachingme

@youcantryreachingme Tôi không biết tại sao SQL Server yêu cầu các lô này phải riêng biệt. Nếu bạn không thích hành vi này, tôi cho rằng bạn có thể nhập một số phản hồi cho nhóm SQL Server. Nó có vẻ là một yêu cầu kỳ lạ đối với tôi, nhưng tôi chắc chắn rằng có một lý do kỹ thuật cho nó.
Paul Williams

@PaulWilliams - Sau khi giải đáp tất cả thắc mắc của mình trong phần nhận xét ở đây (và trên trao đổi ngăn xếp dba), tôi tiếp tục xem qua các tài liệu của Microsoft để thử và hiểu. Tôi nghĩ rằng "tại sao" - các lý do - xung quanh các lô và gophụ thuộc vào việc hiểu ý nghĩa của việc phân lô - chủ yếu là một kế hoạch thực hiện duy nhất được chuẩn bị từ toàn bộ lô. Vì vậy, cuối cùng tôi đã quyết định đóng góp câu trả lời của riêng mình để thử và giải thích "tại sao" - xem bên dưới, tại đây: stackoverflow.com/a/56370223/3714936
youcantryreachingme

14

Có ích gì khi có bộ tách lô?

Sau khi đọc nhiều câu trả lời và đóng góp ý kiến, đây là những gì tôi nghĩ.

Câu hỏi thực sự là "Có ích lợi gì khi có một mẻ?"

Có 2 hàm ý của việc phân lô có một số ý nghĩa và có một cách sử dụng bổ sung gocó thể hữu ích:

1. Tất cả các câu lệnh trong một lô được biên dịch thành một kế hoạch thực thi duy nhất

Điều này ảnh hưởng đến bạn như thế nào, với tư cách là một nhà phát triển SQL, tôi không biết. Nhưng nó đây rồi. Ý nghĩa của điều này là bạn không thể có một số câu lệnh trong cùng một lô . Ví dụ, bạn không thể ALTERthêm một bảng để thêm một cột, sau selectđó cột đó trong cùng một lô - bởi vì trong khi biên dịch kế hoạch thực thi, cột đó không tồn tại để chọn.

Tôi nghĩ rằng có một đối số mở về việc liệu SQL Server có thể tự phát hiện điều này mà không yêu cầu nhà phát triển đưa các gocâu lệnh vào tập lệnh của họ hay không. Hơn nữa, các tài liệu cho biết kết nối ODBC có thể không bao giờ đưa ra golệnh. Tôi không rõ một tập lệnh chạy qua ODBC sẽ hoạt động như thế nào nếu nó bao gồm ALTER/ SELECTví dụ vừa đưa ra.

2. Các biến được khai báo cục bộ chỉ tồn tại trong phạm vi của lô mà chúng được khai báo

Hai điểm này kết hợp kiểu hút. Tôi có một tập lệnh tạo và thay đổi cấu trúc DB (bảng, thủ tục, v.v.) và tôi muốn khai báo các biến ở đầu tập lệnh sẽ được sử dụng để điều chỉnh hành vi của tập lệnh tổng thể. Ngay khi tôi cần kết thúc một lô (ví dụ, một ALTERcâu lệnh - xem điểm 1 của tôi ở trên), các biến "cấu hình" đó nằm ngoài phạm vi và không thể sử dụng thêm trong tập lệnh. Cách giải quyết của tôi là tạo một bảng, duy trì các biến cấu hình trong bảng, sau đó đọc từ bảng đó toàn bộ thông qua tập lệnh của tôi, sau đó thả bảng ở cuối (trong trường hợp bất kỳ ai khác đang phải đối mặt với điều này).

Hàm ý thứ hai này thực sự có thể được sử dụng để tạo lợi thế - nếu tập lệnh của bạn đang thực hiện nhiều công việc và bạn chỉ muốn xóa tất cả các biến cục bộ của mình, bạn có thể chỉ cần bao gồm một GOcâu lệnh và sau đó khai báo các biến mới (tức là và sử dụng lại cùng tên, nếu đó là những gì bạn muốn).

3. GO có một tham số tùy chọn (có tên "count") cho máy chủ lặp lại các hành động hàng loạt nhiều lần

Việc sử dụng này dường như là một chức năng bổ sung tuyệt vời được thêm vào GOcâu lệnh. Tôi tin rằng chức năng ban đầu hoặc chức năng chính của GOliên quan nhiều hơn đến việc biên dịch một kế hoạch thực thi đơn lẻ, như đã đề cập ở điểm 1 - nếu không thì từ khóa cũng có thể giống như thế REPEAT 10- nhưng hãy lặp lại điều gì? Lô. Không GObiểu thị một lô, một lệnh lặp chỉ có thể lặp lại câu lệnh đơn trước đó. Do đó GOlà một cách tốt đẹp để lặp lại hàng loạt .

Tài liệu tham khảo

Tất cả những điều này đến từ việc cố gắng hiểu tài liệu MS về GO . Nhiều câu trả lời khác - ở đây và các câu hỏi khác - chọn từ các phần của tài liệu nhưng tôi nghĩ rằng bản thân tài liệu không thực sự giải thích được tại sao lại có lợi cho việc phân lô ngay từ đầu - do đó, đóng góp của tôi cho một câu hỏi.

Phụ lục

Sau khi viết phần trên, tôi đã tìm thấy Quy tắc sử dụng hàng loạt được Microsoft đề cập trong GOtài liệu. Trang được liên kết giải thích rằng một kế hoạch thực thi bao gồm nhiều câu lệnh. Nó cũng nói rằng các câu lệnh riêng lẻ có thể được biên dịch lại thành một kế hoạch thực thi mới (tức là bằng SQL Server, trong khi xử lý hàng loạt, tự động). Ví dụ: sau một câu lệnhCREATE TABLE bạn có thể có một INSERTtrong bảng đó. Câu INSERTlệnh đó sẽ được biên dịch lại sau khi bảng đã được tạo trong câu lệnh trước.

Điều này thực thi lại ý tưởng rằng SQL Server có thể có thể phát hiện những trường hợp trong đó ALTERmột bảng được theo sau bởi một SELECTvà rằng nó cần phải biên dịch lại SELECT(xem điểm 1 của tôi ở trên) và có thể đây chính xác là những gì sẽ xảy ra nếu sử dụng ODBC (xem điểm 1 ở trên).

Không có thông tin mới nào thay đổi 3 điểm đã nêu ở trên. Liên kết tôi vừa cung cấp chứa phần đọc bổ sung và kết thúc bằng "các quy tắc", đó là:

  • Các câu lệnh TẠO DEFAULT, TẠO CHỨC NĂNG, TẠO QUY TRÌNH, TẠO QUY TẮC, TẠO SCHEMA, TẠO TRIGGER và TẠO XEM không thể kết hợp với các câu lệnh khác trong một lô. Câu lệnh CREATE phải bắt đầu lô. Tất cả các câu lệnh khác tiếp theo trong lô đó sẽ được hiểu là một phần của định nghĩa câu lệnh CREATE đầu tiên.

  • Một bảng không thể được thay đổi và sau đó các cột mới được tham chiếu trong cùng một lô.

  • Nếu một câu lệnh EXECUTE là câu lệnh đầu tiên trong một lô, thì từ khóa EXECUTE không bắt buộc. Từ khóa EXECUTE là bắt buộc nếu câu lệnh EXECUTE không phải là câu lệnh đầu tiên trong lô.


Điều này vô cùng hữu ích. Cảm ơn bạn đã dành thời gian để chia sẻ những gì bạn học được một cách có tổ chức.
NetherGranite

5

Giống như Martain đã nói, những tuyên bố như TẠO THỦ TỤC phải là những tuyên bố duy nhất trong một đợt.

Ví dụ: tôi sử dụng dấu phân tách hàng loạt bất cứ khi nào tôi tạo các thủ tục được lưu trữ và thêm quyền cho một người dùng nhất định. Nếu tôi bỏ qua 'go' thì tôi sẽ kết thúc với một thủ tục được lưu trữ cấp quyền mỗi khi nó chạy. Bằng cách này, tôi có thể viết chúng cùng một lúc và chắc chắn rằng tôi không viết các thủ tục đã lưu trữ bị hỏng khi tôi gọi chúng. Ví dụ

create procedure [procedurename]
(parameters)
as begin

select prefname, lastname from people

end

go

grant execute on [procedurename] to [username]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.