Як зламати виконання сценарію SQL


16

Я працюю над сценарієм sql, і у мене виникає вимога припинити продовження сценарію, якщо деякі умови не виконуються.

Коли я Google це відкрив, я виявив, що RaisError з 20 рівнем тяжкості припинить його. Але я чомусь не можу використовувати цей варіант.

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


1
Чому виникнення помилки неприпустимо? Чи цей сценарій є збереженою процедурою?
Намфібієць

Я чітко не зрозумів вашого кулакового питання. Для другого питання; ні це не СП
Новий розробник

1
Що таке сценарій? Чи складається воно з декількох партій? Ви бачили тут відповіді?
Мартін Сміт

Відповіді:


8

З документації RAISERROR (міна акценту):

Рівень тяжкості від 0 до 18 може визначати будь-який користувач. Рівні ступеня вираженості від 19 до 25 можуть визначати лише члени ролі фіксованого сервера sysadmin або користувачі з дозволом ALTER TRACE. Для рівнів тяжкості від 19 до 25 потрібен варіант ЗНО ЛОГ.

Цілком ймовірно, що головний, який ви виконуєте сценарій, не відповідає цим критеріям.

У використанні немає нічого поганого RAISERROR; ви просто використовуєте надмірний ступінь вираженості. Я використовую рівень 16 за замовчуванням для помилки, яка піднімається, і послідовність буде припинена. Якщо ви хочете бути більш точними, ви можете дотримуватися рівнів, заданих самим Microsoft:

введіть тут опис зображення

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

Наприклад:

RAISERROR(N'Test', 16, 1);

SELECT 1;   /* Executed! */

Це і викличе помилку і повертає набір результатів.

Для негайного припинення роботи сценарію я вважаю за краще використовувати RETURN(використання GOTOконструкцій типу, як правило, не рекомендується в більшості програмних кіл, де існують альтернативи):

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */

Або обробляйте помилку, використовуючи TRY/CATCH, що призведе до того, що виконання може перейти до CATCHблоку, якщо ступінь тяжкості становить 11 або вище:

BEGIN TRY
    RAISERROR(N'Test', 16, 1);
    SELECT 1;   /* Not executed */
END TRY
BEGIN CATCH
    SELECT 2;   /* Executed */
END CATCH

BEGIN TRY
    RAISERROR(N'Test', 10, 1);
    SELECT 1;   /* Executed */
END TRY
BEGIN CATCH
    SELECT 2;   /* Not executed */
END CATCH

Окрема проблема полягає в тому, якщо сценарій охоплює декілька пакетів - RETURNвийде лише з партії :

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */
GO

SELECT 2;   /* Executed! */

Щоб виправити це, ви можете перевірити @@ERRORна початку кожної партії:

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */
GO

IF (@@ERROR != 0)
    RETURN;

SELECT 2;   /* Not executed */

Редагувати: Як правильно вказує в коментарях Мартін Сміт, це працює лише для 2 партій. Щоб розширити на 3 і більше партій, ви можете каскадно піднімати помилки на зразок (зверніть увагу: GOTOметод не вирішує цю проблему, оскільки цільова мітка повинна бути визначена в пакеті):

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */
GO

IF (@@ERROR != 0)
BEGIN
    RAISERROR(N'Error already raised. See previous errors.', 16, 1);
    RETURN;
END

SELECT 2;   /* Not executed */
GO

IF (@@ERROR != 0)
BEGIN
    RAISERROR(N'Error already raised. See previous errors.', 16, 1);
    RETURN;
END

SELECT 3;   /* Not executed */

Або, як він також вказує, ви можете використовувати SQLCMDметод, якщо це підходить для вашого оточення.


Остання пропозиція не працює. Дивіться пастебін . Мені тут подобається метод sqlcmd
Мартін Сміт

6

Ви можете використовувати GOTOоператор, щоб пропустити куди завгодно. Іншими словами, ви зіткнулися з помилкою чи якоюсь іншою умовою, і ви можете мати мітку внизу скрипту (тобто TheEndOfTheScript:) і просто видати goto TheEndOfTheScript;заяву.

Ось короткий зразок:

print 'here is the first statement...';

print 'here is the second statement...';

-- substitute whatever conditional flow determining factor
-- you'd like here. I have chosen a dummy statement that will
-- always return true
--
if (1 = 1)
    goto TheEndOfTheScript;

print 'here is the third statement...';

print 'here is the fourth statement...';


TheEndOfTheScript:
print 'here is the end of the script...';

Вихід цього виконання буде наступним:

here is the first statement...
here is the second statement...
here is the end of the script...

Як бачимо, GOTOвипустив друк третього та четвертого висловлювань і перейшов праворуч до мітки ( TheEndOfTheScript).


7
Працює лише тоді, коли є одна партія, вона порушиться, як тільки у вас з'явиться заява GO.
Габріель


0

Погодьтеся з тим SET NOEXEC ON/OFF, що в Stored Procs (що містить один блок) я просто використовую RETURNоператор.

Застереження: у файлі сценарію, якщо у вас є декілька GOвисловлювань, RETURNвийде лише з поточного блоку і продовжить наступний блок / пакет.

Примітка. GOTOЦе, як вважається, є поганою практикою кодування, рекомендується використовувати " TRY..CATCH", оскільки це було запроваджено після запуску SQL Server 2008, а потім THROWу 2012 році.

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