TSQL - Як використовувати GO всередині блоку BEGIN .. END?


96

Я створюю скрипт для автоматичного перенесення змін з декількох баз даних розробки в індексування / виробництво. В основному, для цього потрібна купа сценаріїв змін і об’єднується в один сценарій, обертаючи кожен сценарій у IF whatever BEGIN ... ENDвиписку.

Однак деякі сценарії вимагають GOоператора, щоб, наприклад, аналізатор SQL знав про новий стовпець після його створення.

ALTER TABLE dbo.EMPLOYEE 
ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
GO -- Necessary, or next line will generate "Unknown column:  EMP_IS_ADMIN"
UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever

Однак, як тільки я загортаю це в IFблок:

IF whatever
BEGIN
    ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
    GO
    UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever
END

Це не вдається, тому що я надсилаю BEGINбез відповідності END. Однак, якщо я видалю це, GOвін знову скаржиться на невідому колонку.

Чи є спосіб створити та оновити один і той же стовпець у межах одного IFблоку?


2
Див stackoverflow.com/questions/4855537 / ... будь ласка
ГБН

2
@gbn: Так, я розумію, чому це відбувається (див. другий абзац) ; але я не уявляю, як це обійти - чи справді мені потрібно перетворювати кожен запит на купу рядків !?
BlueRaja - Danny Pflughoeft

@BlueRaja: У чому проблема? Якщо це працює, це все, що має значення в кінці дня. Якщо із запропонованим рішенням існує законна ділова проблема, будь ласка, висловіть це. Чи є щось конкретне, що бентежить у перетворенні кожного запиту в купу рядків?
mellamokb

1
@mellamokb: Так, є проблема; якщо слово GO використовується в будь-якому іншому контексті (наприклад, коментарі чи рядку), сценарій не працюватиме. Крім того, ми втрачаємо корисні номери рядків у повідомленнях про помилки, якщо щось піде не так. Чи неможливо це зробити з транзакціями? Або спробувати / зловити?
BlueRaja - Danny Pflughoeft

@BlueRaja: 1) Я вважаю, що GOвін повинен бути на рядку сам по собі, тому ви можете шукати лише цей випадок, а не кожен приклад слова GO. 2) Ви завжди можете записати, які оператори були успішно виконані. Або ви можете обернути все це у спробі / лові та використовувати власні номери рядків, використовуючи якусь змінну, наприклад @lineNo, яку ви відстежуєте та повідомляєте про помилку. Оскільки ви генеруєте їх автоматично, внесення подібних змін повинно бути легким. Це просто схоже на те, що ви, звичайно, не хочете досліджувати цей маршрут, коли, на мою думку, є шляхи вирішення всіх ваших проблем.
mellamokb

Відповіді:


44

У мене була та сама проблема, і нарешті вдалося її вирішити за допомогою SET NOEXEC .

IF not whatever
BEGIN
    SET NOEXEC ON; 
END

ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
GO
UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever

SET NOEXEC OFF; 

2
Це чудове рішення!
Bazinga

+1! Наразі це ЄДИНА практична відповідь для використання в SQLCMDсценарії режиму SS (тобто головний сценарій розгортання), який викликає (за допомогою :rкоманди) інші сценарії SS (тобто сценарії додаткового розгортання) з деякими з цих викликів усередині ifStatements. Відповіді Одеда, Мелламока та Енді Джойнєра всіх цих заяв у execдзвінки / begin- endне є початковими. Крім того , begin- endметод не буде працювати , якщо є createзаява (наприклад , вимагає явного goдо нього). Але, чоловіче, "Святий подвійний негатив, Бетмене!" ;)
Том

Для читабельності (наприклад, щоб допомогти подолати подвійні негативи та зробити зрозумілішим, що він імітує віртуальний if -блок), я б до префікса блоку додав -- If whateverКоментар, відступ блоку та постфікс блоку --end If whateverКоментарем.
Том

Ти врятував мій бекон! Я
запускав операції

Гм, я отримую повідомлення про помилку оновлення якось після того, як було встановлено noexec on? (помилка, що ім'я стовпця для оновлення є недійсним) Запуск у MSSQL 2014 у редакторі запитів. Працює нормально, якщо умова стає хибною (таким чином noexec залишається вимкненим)
Джеррі

43

GO не є SQL - це просто пакетний роздільник, який використовується в деяких інструментах MS SQL.

Якщо ви цього не використовуєте, вам потрібно переконатися, що оператори виконуються окремо - або різними пакетами, або за допомогою динамічного SQL для популяції (спасибі @gbn):

IF whatever
BEGIN
    ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL;

    EXEC ('UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever')
END

8
Так, я це розумію. Це не відповідає на питання - мені потрібно створити та оновити стовпець у тому самому IFблоці.
BlueRaja - Danny Pflughoeft

@Oded: Чи ;допомогла б тут? - Ви щойно відредагували свою відповідь: o)
Ніл Найт,

@Neil - це моє мислення, так.
Оді

;теж не працює - парсер все ще дає мені "Неправильне ім'я стовпця 'EMP_IS_ADMIN'."
BlueRaja - Danny Pflughoeft

Коли компіляція пакета, EMP_IS_ADMIN не існує. stackoverflow.com/questions/4855537 / ...
ГБН

16

Ви можете спробувати sp_executesql , розділивши вміст між кожним GOоператором на окремий рядок для виконання, як показано в прикладі нижче. Крім того, існує змінна @statementNo для відстеження того, який оператор виконується для легкого налагодження, де сталося виняток. Номери рядків будуть відносно початку відповідного номера виписки, що спричинив помилку.

BEGIN TRAN

DECLARE @statementNo INT
BEGIN TRY
    IF 1=1
    BEGIN
        SET @statementNo = 1
        EXEC sp_executesql
            N'  ALTER TABLE dbo.EMPLOYEE
                    ADD COLUMN EMP_IS_ADMIN BIT NOT NULL'

        SET @statementNo = 2
        EXEC sp_executesql
            N'  UPDATE dbo.EMPLOYEE
                    SET EMP_IS_ADMIN = 1'

        SET @statementNo = 3
        EXEC sp_executesql
            N'  UPDATE dbo.EMPLOYEE
                    SET EMP_IS_ADMIN = 1x'
    END
END TRY
BEGIN CATCH
    PRINT 'Error occurred on line ' + cast(ERROR_LINE() as varchar(10)) 
       + ' of ' + 'statement # ' + cast(@statementNo as varchar(10)) 
       + ': ' + ERROR_MESSAGE()
    -- error occurred, so rollback the transaction
    ROLLBACK
END CATCH
-- if we were successful, we should still have a transaction, so commit it
IF @@TRANCOUNT > 0
    COMMIT

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


Не думайте, що це буде працювати для команд, розділених на кілька рядків, чи не так?
BlueRaja - Danny Pflughoeft

@BlueRaja: Я оновив приклад, щоб показати, як це буде працювати. Ці рядки можуть бути багаторядковими, доки будь-які одинарні лапки ('), що містяться всередині, уникаються за допомогою подвійної одинарної лапки (' ')
mellamokb

1
@mellamokb: строго кажучи, тільки оновлення необхідно sp_executesql ... stackoverflow.com/questions/4855537 / ...
ГБН

1
@gbn: Правда. Але якщо ви збираєтеся автоматизувати це для 100 тверджень, буде простіше просто застосувати його наосліп до всіх тверджень, замість того, щоб вирішувати, коли і де це потрібно.
mellamokb

@gbn @mellamokb: Я мав на увазі такі твердження SELECT * <newline> FROM whatever. Якщо я виконую кожен рядок із власною заявою EXEC, це зламається. Або ви пропонуєте зламати кожне GOтвердження?
BlueRaja - Danny Pflughoeft

9

Зрештою, я змусив це працювати, замінивши кожен екземпляр у GOсвоєму рядку на

END
GO

---Automatic replacement of GO keyword, need to recheck IF conditional:
IF whatever
BEGIN

Це набагато краще, ніж обгортання кожної групи тверджень у рядок, але все ще далеке від ідеалу. Якщо хтось знайде краще рішення, опублікуйте його, і я натомість прийму його.


6
Якщо першим умовним є "якщо цей стовпець не існує", першим оператором у блоці є "додати цей стовпець", тоді друга перевірка умовного знайде стовпець і не виконає другий оператор,
Damien_The_Unbeliever

@Damien: Правда; на щастя, це ніколи не відбудеться в моєму випадку (умовна - це завжди перевірка на певне значення в конкретній таблиці, яка завжди додається як остання інструкція IFблоку). Здається, що в SQL просто немає хорошого способу зробити це.
BlueRaja - Danny Pflughoeft

Відповідь Міни Джейкоб set noexec- це ЄДИНИЙ практичний відповідь на даний момент для використання в SQLCMDсценарії режиму SS (тобто головний сценарій розгортання), який викликає (за допомогою :rкоманди) інші сценарії SS (тобто сценарії додаткового розгортання) з деякими з цих викликів усередині ifStatements. Відповіді Oded, mellamokb та Andy Joiner щодо включення всіх цих заяв у execдзвінки / begin- endне є початковими. Крім того , begin- endметод не буде працювати , якщо є createзаява (наприклад , вимагає явного goдо нього).
Том

8

Ви можете вкласти заяви в BEGIN та END замість GO між ними

IF COL_LENGTH('Employees','EMP_IS_ADMIN') IS NULL --Column does not exist
BEGIN
    BEGIN
        ALTER TABLE dbo.Employees ADD EMP_IS_ADMIN BIT
    END

    BEGIN
        UPDATE EMPLOYEES SET EMP_IS_ADMIN = 0
    END
END

(Перевірено на базі даних Northwind)

Редагувати: (Можливо, перевірено на SQL2012)


1
Будь ласка, вкажіть причину -1
Andy Joiner

1
Не знаю, чому це було проголосовано ... для мене це працює як шарм.
Торарін

10
Використовуючи SQL Server 2008 R2, це, здається, не працює для мене, я все одно отримую повідомлення про помилку "Неправильне ім'я стовпця" EMP_IS_ADMIN "." у рядку UPDATE.
MerickOWA

Пакетний пакет BEGIN-END працював для мене за допомогою SQL Server 2016. IMO - це найчистіший синтаксис.
Uber Schnoz

Відповідь Міни Джейкоб set noexec- це ЄДИНИЙ практичний відповідь на даний момент для використання в SQLCMDсценарії режиму SS (тобто головний сценарій розгортання), який викликає (за допомогою :rкоманди) інші сценарії SS (тобто сценарії додаткового розгортання) з деякими з цих викликів усередині ifStatements. Відповіді Oded, mellamokb та Andy Joiner щодо включення всіх цих заяв у execдзвінки / begin- endне є початковими. Крім того , begin- endметод не буде працювати , якщо є createзаява (наприклад , вимагає явного goдо нього).
Том

1

Ви можете спробувати це рішення:

if exists(
SELECT...
)
BEGIN
PRINT 'NOT RUN'
RETURN
END

--if upper code not true

ALTER...
GO
UPDATE...
GO

1
Не дуже корисно, якщо у вас є кілька блоків if-else один за одним, так?
Джеррі

0

Я використовував RAISERRORу минулому для цього

IF NOT whatever BEGIN
    RAISERROR('YOU''RE ALL SET, and sorry for the error!', 20, -1) WITH LOG
END

ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
GO
UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever

-1

Ви можете включити оператори a GOTOта LABELоператори, щоб пропустити код, таким чином залишаючи GOключові слова недоторканими.


5
Здається, на LABEL не можна посилатись у операторах GO, оскільки їх немає в пакеті, надісланому до SQL
berkeleybross
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.