Зробіть цикл у SQL Server 2008


120

Чи є який-небудь метод для виконання do whileциклу на SQL сервері 2008?


5
Відповідь, яку дав Рахул, є правильною, але чого саме ви намагаєтесь досягти? Петлі є дорогими порівняно з набором рішень на основі. Можливо, цілком можна уникнути циклу.
Lieven Keersmaekers

Не використовуйте циклів, якщо це взагалі можливо, і я вважаю, що 95% часу і більше можливо їх уникнути. Петлі та курсори є вбивцями продуктивності, і ніколи їх не повинен писати хтось, хто не є досвідченим DBA, що має принаймні п’ять років налаштування продуктивності.
HLGEM

1
Ер. трохи драматичні там HLGEM, петлі та курсори насправді дуже акуратні, поки ви не перебираєте кожен ряд в таблиці. Якщо у вас є список категорій або сайтів або щось порівняно високого рівня, то цикл цілком може бути найефективнішим способом запуску запиту.
Джефф Грісвальд

Відповіді:


190

Я не впевнений у DO-WHILE в MS SQL Server 2008, але ви можете змінити свою логіку циклу WHILE, щоб використовувати, як цикл DO-WHILE.

Приклади взяті звідси: http://blog.sqlauthority.com/2007/10/24/sql-server-simple-example-of- while-loop-with-continue-and-break-keywords/

  1. Приклад циклу WHILE

    DECLARE @intFlag INT
    SET @intFlag = 1
    WHILE (@intFlag <=5)
    BEGIN
        PRINT @intFlag
        SET @intFlag = @intFlag + 1
    END
    GO

    Набір результатів:

    1
    2
    3
    4
    5
  2. Приклад циклу WHILE з ключовим словом BREAK

    DECLARE @intFlag INT
    SET @intFlag = 1
    WHILE (@intFlag <=5)
    BEGIN
        PRINT @intFlag
        SET @intFlag = @intFlag + 1
        IF @intFlag = 4
            BREAK;
    END
    GO

    Набір результатів:

    1
    2
    3
  3. Приклад циклу WHILE з ключовими словами CONTINUE та BREAK

    DECLARE @intFlag INT
    SET @intFlag = 1
    WHILE (@intFlag <=5)
    BEGIN
        PRINT @intFlag
        SET @intFlag = @intFlag + 1
        CONTINUE;
        IF @intFlag = 4 -- This will never executed
            BREAK;
    END
    GO

    Набір результатів:

    1
    2
    3
    4
    5

Але намагайтеся уникати циклів на рівні бази даних. Довідково .


17
Ці ж приклади наведено тут, ви автор цього веб-сайту? blog.sqlauthority.com/2007/10/24/…
анар халілов

1
Він ні, але чому це важливо? Дано правильну відповідь. Посилання на інший веб-сайт - це біль, копіювання та вставлення відповіді тут є корисним.
Джефф Грісвальд

61

Якщо ви не дуже ображаєтеся на GOTOключове слово, його можна використовувати для імітації DO/ WHILEв T-SQL. Розглянемо наступний досить безглуздий приклад, написаний псевдокодом:

SET I=1
DO
 PRINT I
 SET I=I+1
WHILE I<=10

Ось еквівалентний код T-SQL за допомогою goto:

DECLARE @I INT=1;
START:                -- DO
  PRINT @I;
  SET @I+=1;
IF @I<=10 GOTO START; -- WHILE @I<=10

Зауважте відображення "один на один" між GOTOувімкненим рішенням та оригінальним DO/ WHILEпсевдокодом. Схожа реалізація з використанням WHILEциклу виглядатиме так:

DECLARE @I INT=1;
WHILE (1=1)              -- DO
 BEGIN
  PRINT @I;
  SET @I+=1;
  IF NOT (@I<=10) BREAK; -- WHILE @I<=10
 END

Тепер ви, звичайно, можете переписати цей конкретний приклад як простий WHILEцикл, оскільки це не такий хороший кандидат для DO/ WHILEконструкту. Акцент робився на прикладі стислості, а не застосовності, оскільки обгрунтовані випадки, які потребують DO/, WHILEє рідкісними.


REPEAT / UNTIL, хтось (НЕ працює в T-SQL)?

SET I=1
REPEAT
  PRINT I
  SET I=I+1
UNTIL I>10

... і GOTOбазове рішення в T-SQL:

DECLARE @I INT=1;
START:                    -- REPEAT
  PRINT @I;
  SET @I+=1;
IF NOT(@I>10) GOTO START; -- UNTIL @I>10

Завдяки творчому використанню GOTOта логічній інверсії за допомогою NOTключового слова існує дуже тісний взаємозв'язок між оригінальним псевдокодом та базовим GOTOрішенням. Схоже рішення за допомогою WHILEциклу виглядає так:

DECLARE @I INT=1;
WHILE (1=1)       -- REPEAT
 BEGIN
  PRINT @I;
  SET @I+=1;
  IF @I>10 BREAK; -- UNTIL @I>10
 END

Аргумент може бути зроблено , що для випадку з REPEAT/ UNTIL, то WHILEрішення на основі простіше, тому що , якщо умова не інвертується. З іншого боку, це також більш багатослівно.

Якщо б не було зневаги навколо використання GOTO, це може бути навіть ідіоматичним рішенням для тих кількох разів, коли ці конкретні (злі) циклічні конструкції необхідні в коді T-SQL для наочності.

Використовуйте їх на власний розсуд, намагаючись не зазнавати гніву ваших колег-розробників, коли вони зловили вас, використовуючи сильно злісних GOTO.


6
+1: однозначно відповідає на питання краще, ніж прийнята відповідь.
Луї Котманн

Я віддаю перевагу підходу WHILE (1 = 1), оскільки він функціонально відповідає цілі, не використовуючи жахливого GOTO.
kad81

Обидва методи дійсні. Мені подобається псевдо-петля за допомогою GOTO, це досить розумно.
Джефф Грісвальд

18

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

Зазвичай, коли я думаю, що мені знадобиться DO WHILET-SQL, це тому, що я ітераюю курсор, і я в основному шукаю оптимальної чіткості (порівняно з оптимальною швидкістю). У T-SQL, здається, відповідає WHILE TRUE/ IF BREAK.

Якщо це сценарій, який привів вас сюди, цей фрагмент може врятувати вам хвилину. Інакше, ласкаво просимо, я. Тепер я можу бути впевнений, що тут був не раз. :)

DECLARE Id INT, @Title VARCHAR(50)
DECLARE Iterator CURSOR FORWARD_ONLY FOR
SELECT Id, Title FROM dbo.SourceTable
OPEN Iterator
WHILE 1=1 BEGIN
    FETCH NEXT FROM @InputTable INTO @Id, @Title
    IF @@FETCH_STATUS < 0 BREAK
    PRINT 'Do something with ' + @Title
END
CLOSE Iterator
DEALLOCATE Iterator

На жаль, T-SQL, здається, не пропонує більш чіткого способу однозначного визначення циклу, ніж цей нескінченний цикл.


Використання курсору ніколи не є хорошим варіантом, оскільки для цього потрібні набагато більше ресурсів.
greektreat

10
@greektreat: Дякую за голосування :), але я заплутався! Якщо "курсор ніколи не є хорошим варіантом", то він завжди повинен бути хорошим варіантом, так навіщо знищення? Однак, якщо серйозно, то, звичайно, курсори мають багато практичних застосувань: проти приватних таблиць, для невеликих операцій, де чіткість / стислість> продуктивність, для завдань технічного обслуговування, дедетерміновані операції недоступні, для певних операцій повинні відбуватися як атомна транзакція незалежно тощо. У своєму недавньому випадку я вводив змінну вхідної таблиці, приватну моїй збереженій процедурі. Абсолютна рівність ніколи не є доброю ідеєю!
Шеннон

5
@greektreat: підсумовуючи, іноді ітераційні дані - це ТІЛЬКИ варіант. Я гадаю, ви все ще можете стверджувати, що це не гарний варіант у такому випадку, але це не означає, що такий код є непотрібним і потребує зворотного аналізу.
Шеннон

1
Я думаю, в Інтернеті є шалена армія людей, які дуже і дуже розлючені на інших людей, що використовують петлі та курсори в SQL. На цьому веб-сайті, якщо ви навіть так багато згадуєте про використання циклу в SQL приблизно через 30 секунд, ваша
поштова

4

Ви також можете використовувати змінну виходу, якщо хочете, щоб ваш код був трохи більш читабельним:

DECLARE @Flag int = 0
DECLARE @Done bit = 0

WHILE @Done = 0 BEGIN
    SET @Flag = @Flag + 1
    PRINT @Flag
    IF @Flag >= 5 SET @Done = 1
END

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


Я маю на увазі ... Ми живемо в епоху, коли наші сервери баз даних мають будь-який час між 10 і 20 ядрами процесора в режимі очікування в будь-який момент, і коли наші контролери зберігання мають пропускну здатність, виміряну в Гігабітах, тому я не впевнений, що це звичайне " мудрість ", що" LOOP = BAD "все ще застосовується.
Джефф Грісвальд

1

Тільки поки цикл офіційно підтримується сервером SQL. Вже є відповідь за DO while loop. Я деталізую відповідь про способи досягнення різних типів циклів на SQL сервері.

Якщо ви знаєте, вам потрібно все-таки виконати першу ітерацію циклу, тоді ви можете спробувати DO..WHILE або REPEAT..UNTIL версію SQL-сервера.

ЗРОБИТИ ШЛЯХ

DECLARE @X INT=1;

WAY:  --> Here the  DO statement

  PRINT @X;

  SET @X += 1;

IF @X<=10 GOTO WAY;

ПОВТОРЕННЯ..НЕТИЛЬНА петля

DECLARE @X INT = 1;

WAY:  -- Here the REPEAT statement

  PRINT @X;

  SET @X += 1;

IFNOT(@X > 10) GOTO WAY;

ДЛЯ петлі

DECLARE @cnt INT = 0;

WHILE @cnt < 10
BEGIN
   PRINT 'Inside FOR LOOP';
   SET @cnt = @cnt + 1;
END;

PRINT 'Done FOR LOOP';

Довідково


Це здається копіюванням-вставкою-перестановкою з stackoverflow.com/a/46362450/8239061 .
SecretAgentMan

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