SQL Server: для чого корисні пакетні оператори (тобто використання «GO»)?


77

Я знаю, що в SQL Server GO вважається пакетним роздільником .

Моє запитання: який сенс мати сепаратор партії? Яку користь це приносить вам і чому ви хочете цим скористатися?

Приклад: Я часто бачив, як він використовується в коді SQL наступним чином, і я не можу зрозуміти, чому це буде вважатися найкращою практикою. Наскільки я можу сказати, код буде однаковим без усіх GOтверджень:

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

(джерело: документація technet ):



7
@JohnnyBones, ні, це не те саме (я навіть поставив посилання на цю публікацію у своєму питанні). Цей пост в основному говорить про те, що GO - це сепаратний сепаратор, але моє запитання полягає в тому, для чого корисний сепаратор партій
Zain Rizvi

Подивіться на відповідь, надану tvanfosson у цьому питанні (26 голосів проти).
Johnny Bones

3
@JohnnyBones дякую, що вказали на цю відповідь. Це дає хорошу додаткову довідкову інформацію, але не відповідає на кореневе запитання, яке у мене було (чорт візьми, одна людина навіть залишила коментар до цього питання, запитуючи, для чого корисні пакетні сценарії)
Zain Rizvi

Безумовно, дублікат цього ... stackoverflow.com/questions/2668529/t-sql-go-statement/…
sam yi

Відповіді:


41

У прикладі немає ніякої користі.

Багато висловлювань, мабуть, є єдиними в пакеті.

Такі як CREATE PROCEDURE.

Також часто після внесення змін у схему (наприклад, додавання нового стовпця до існуючої таблиці) оператори з використанням нової схеми повинні бути скомпільовані окремо в інший пакет.

Як правило, альтернативою подачі окремих пакетів, розділених, GOє виконання SQL у дочірній партії за допомогоюEXEC


1
Це має сенс, дякую. Я також бачив, як він використовується у сценаріях оновлення схеми бази даних після кожної окремої операції зміни / створення таблиці, навіть коли послідовні операції не залежать від попередньої. Чи є якась причина для цього, або це було б практикою, щоб не робити розробникам доводиться добре думати над своїм SQL
Zain Rizvi

1
@Zain - Ну, це не приносить великої шкоди (крім збільшення мережевих кругових поїздок) і, ймовірно, зберігає деякі помилки там, де операції залежать від попередніх.
Martin Smith

Здається, код SQL потрібно буде розділити на дві групи, коли друга партія оперує об'єктами, створеними першою партією. Іншими словами, коли друга партія вимагає, щоб механізм планування SQL використовував артефакти, які ще не існують, що призведе до псування плану. Це правильно?
Zain Rizvi

7
@Zain - Що стосується таблиць, то зміни в існуючих таблицях, як правило, є більш проблематичними. CREATE TABLE T(X INT);SELECT * FROM Tчудово працює, оскільки SELECTпідлягає відкладеній компіляції. Але тоді після створення таблиці ALTER TABLE T ADD Y INT;SELECT Y FROM Tне вдасться з помилкою помилки в назві стовпця, оскільки цей оператор не отримує відкладеного компілювання.
Мартін Сміт

Для мене це все ще не відповідає на питання. "Однак багато заяв повинно бути єдиним у пакеті" - але який сенс мати пакет? Якщо сервер має якесь внутрішнє обмеження, яке вимагає, щоб "багато операторів" було єдиним у пакеті - intellisense може чітко виявити вимогу, то навіщо взагалі вимагати від мене написання GO? Чому б просто не запустити внутрішні органи так, як це потрібно робити? Мартіну - "компіляція відкладена"? Чому кодеру SQL потрібно розуміти процеси внутрішньої компіляції? Залишається питання: у чому сенс дозування?
youcantryreachingme

30

Як каже TechNet , GOце означає кінець пакета SQL для утиліт SQL. Наприклад, коли SQL Server Management Studio зустрічає роздільник пакетів, він знає, що весь текст на даний момент є незалежним запитом SQL.

Ми використовуємо подібну техніку в нашому програмному забезпеченні. Ми зберігаємо всі наші програми, сценарії схем, перетворення даних тощо у файлах сценаріїв SQL (зареєстровані в контролі джерела). Коли наш інсталятор читає один із цих файлів скриптів, GO повідомляє нашому синтаксичному аналізатору "ви можете запустити вже прочитаний SQL".

Приємною особливістю роздільника пакетів, наприклад, GOє те, що ви можете включити два запити SQL разом в один і той же сценарій, що зазвичай спричиняє помилку. Наприклад, спробуйте скинути та заново створити одну і ту ж збережену процедуру в одному файлі сценарію:

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

create procedure sp_test as
begin
    select 1
end

Якщо запустити наведений вище код, ви отримаєте повідомлення про помилку:

Повідомлення 156, рівень 15, стан 1, процедура sp_test, рядок 5 Неправильний синтаксис біля ключового слова 'begin'.

І SSMS покаже вам помилку:

Неправильний синтаксис. "СТВОРИТИ ПРОЦЕДУРУ" має бути єдиним твердженням у пакеті.

Використання роздільника пакетів може допомогти вам обійти цю помилку:

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

Це дуже зручно, якщо, скажімо, ви хочете, щоб один скрипт SQL у вихідному контролі підтримував збережену процедуру або функцію. Ми часто використовуємо цей шаблон.

Ще одна цікава річ, яку ви можете зробити, - це використовувати її для запуску запиту кілька разів:

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

Як показують відповіді на це запитання SO , ви також можете налаштувати його на будь-що, що хочете. Якщо ви хочете поспілкуватися зі своїми колегами, встановіть для сепаратора пакетів щось на зразок "ДЕ", а не "ПЕРЕЙТИ". Весело! :)


13
Така розвага може бути небезпечною для здоров’я.
Ділан Брамс,

2
Ви маєте на увазі, що GO обходить помилку, яка існує лише тому, що GO існує? Якщо Intellisense може виявити GO, потрібно, звичайно, це може зробити і сервер, і просто вирішити проблему. Для мене немає нічого логічно неправильного в тому, що "якщо існує, кинь і після цього створіть". Документи зазначають, що всі оператори в пакеті GO компілюються в єдиний план виконання - саме тому ці оператори зазнають невдачі, але знову ж таки - якщо intellisense може це виявити, безумовно, сервер теж може і просто має з цим справу. go 10насправді є корисним, але не обов’язковим, як goце вимагається у вашому попередньому прикладі.
youcantryreachingme

@youcantryreachingme Я не знаю, чому SQL Server вимагає, щоб ці пакети були окремими. Якщо вам не подобається поведінка, я вважаю, ви можете надіслати відгук для команди SQL Server. Це видається мені дивною вимогою, але я впевнений, що для цього є технічна причина.
Пол Вільямс,

@PaulWilliams - Випустивши всі мої запитання в коментарях тут (і про обмін стеками dba), я продовжував пробивати документи Microsoft, щоб спробувати зрозуміти. Я думаю, що "чому" - причини - навколо партій і goзалежать від розуміння наслідків партії - в першу чергу те, що з усієї партії готується єдиний план виконання. Так що я , нарешті , вирішив внести свій внесок своєї відповіді , щоб спробувати пояснити «чому» - дивіться нижче, тут: stackoverflow.com/a/56370223/3714936
youcantryreachingme

14

Який сенс мати сепаратор партії?

Ознайомившись із багатьма відповідями та надавши коментарі, ось що я думаю.

Справжнє запитання: "Який сенс мати партію?"

Існує 2 наслідки пакетування, які мають певне значення, і існує додаткове використання, goяке може бути корисним:

1. Усі твердження в пакеті складаються в єдиний план виконання

Як це впливає на вас, як розробника SQL, я не знаю. Але воно є. Наслідком цього є те, що ви не можете мати деякі оператори в одній партії . Наприклад, ви не ALTERможете таблицю, щоб додати стовпець, а потім selectцей стовпець у тій самій партії - оскільки під час складання плану виконання цей стовпець не існує для вибору.

Я думаю, що є відкритий аргумент щодо того, чи повинен SQL Server самостійно виявляти це, не вимагаючи від розробників включати goоператори у свої сценарії. Крім того, у документах сказано, що з'єднання ODBC можуть ніколи не видавати goкоманду. Це не для мене ясно , як сценарій запуску через ODBC буде вести себе , якщо він включений в ALTER/ SELECTприклад просто дав.

2. Локально оголошені змінні існують лише в межах групи, в якій вони були оголошені

Ці два моменти поєднували своєрідний відстій. У мене є сценарій, який створює та змінює структури БД (таблиці, процедури тощо), і я хочу оголосити змінні на початку сценарію, які будуть використовуватися для управління поведінкою сценарію загалом. Як тільки мені потрібно завершити пакет (через, скажімо, ALTERтвердження - див. Мій пункт 1 вище), ці змінні "config" виходять за межі області застосування і не можуть бути використані далі в сценарії. Моє обхідне рішення - створити таблицю, зберегти конфігураційні змінні в таблиці, потім прочитати з цієї таблиці весь мій сценарій, а потім опустити таблицю в кінці (на випадок, якщо хтось інший стикається з цим).

Цей другий підтекст насправді можна використати з перевагою - якщо ваш скрипт виконує багато роботи, і ви просто хочете очистити всі свої локальні змінні, ви можете просто включити GOоператор, а потім оголосити нові змінні (тобто. І повторно використовувати ті самі імена, якщо це те, що ви хочете).

3. GO має необов'язковий параметр (з назвою "count"), який повідомляє серверу повторювати пакетні дії кілька разів

Це використання видається приємною додатковою функціональністю, доданою до GOвиписки. Я вважаю, що початкова або основна функція GOбільше стосується компіляції єдиного плану виконання, як згадувалося в пункті 1 - інакше ключове слово також може бути чимось подібним REPEAT 10- але повторити що? Партія. Без GOпозначення партії, команда повторення може лише колись повторити попередній окремий вираз. Тому GOце хороший спосіб повторити партії .

Довідково

Все це відбувається завдяки спробі зрозуміти документацію MS про GO . Багато інших відповідей - і тут, і на інші питання - вибирайте фрагменти документації, але я думаю, що сама документація не може по-справжньому пояснити, чому в першу чергу користь від пакетування - звідси мій внесок у вже добре прокоментований питання.

Додаток

Написавши вище, я знайшов Правила використання пакетів, згадані Microsoft у GOдокументації. На пов’язаній сторінці пояснюється, що план виконання складається з декількох тверджень. У ньому також сказано, що окремі оператори можуть бути повторно скомпільовані в новий план виконання (тобто за допомогою SQL Server під час автоматичної обробки пакета). Так, наприклад, після заяви до CREATE TABLEвас, можливо, ви INSERTпотрапите в цю таблицю. Цей INSERTоператор буде перекомпільовано після того, як таблиця буде створена в попередньому операторі.

Це зміцнює думку про те, що SQL Server, ймовірно, може виявити ті сценарії, коли ALTERза таблицею слідує a, SELECTі що йому потрібно повторно скомпілювати SELECT(див. Мій пункт 1 вище), і, можливо, це саме те, що відбувається, якщо використовується ODBC (див. пункт 1 вище).

Жодна з цих нових відомостей не змінює 3 пункти, наведені вище. Посилання, яке я щойно дав, містить додаткове прочитання і закінчується "правилами", а саме:

  • Оператори CREATE DEFAULT, CREATE FUNCTION, CREATE PROCEDURE, CREATE RULE, CREATE SCHEMA, CREATE TRIGGER і CREATE VIEW не можуть поєднуватися з іншими операторами в пакеті. Оператор CREATE повинен починати пакет. Усі інші твердження, що випливають із цієї групи, будуть інтерпретовані як частина визначення першого оператора CREATE.

  • Таблицю не можна змінити, а потім нові стовпці, на які посилаються в тому ж пакеті.

  • Якщо оператор EXECUTE є першим оператором у пакеті, ключове слово EXECUTE не потрібно. Ключове слово EXECUTE потрібно, якщо оператор EXECUTE не є першим оператором у пакеті.


Це було неймовірно корисно. Дякуємо, що знайшли час, щоб добре організовано поділитися вивченим.
NetherGranite

5

Як сказав Мартен, заяви, такі як СТВОРИТИ ПРОЦЕДУРУ, повинні бути єдиними в групі.

Наприклад, я використовую роздільники пакетів, коли створюю збережені процедури та додаю дозволи певному користувачеві. Якби я пропустив "go", то в підсумку я отримав би збережену процедуру, яка надає права при кожному запуску. Таким чином я можу писати їх одночасно і бути впевненим, що не пишу збережені процедури, які порушуються, коли я їх викликаю. Наприклад

create procedure [procedurename]
(parameters)
as begin

select prefname, lastname from people

end

go

grant execute on [procedurename] to [username]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.