Выполнить цикл while в SQL Server 2008


120

Есть ли какой-либо метод для реализации do whileцикла в SQL Server 2008?


5
Ответ, данный Рахулом, правильный, но чего именно вы пытаетесь достичь? Циклы дороги по сравнению с решениями на основе наборов. Возможно, удастся вообще избежать петли.
— Ливен Кеерсмэкерс

По возможности не используйте петли, и я считаю, что в 95% или более случаев их можно избежать. Циклы и курсоры убивают производительность и никогда не должны быть написаны кем-либо, кто не является опытным администратором баз данных с хотя бы пятилетним опытом настройки производительности.
— HLGEM

1
Er. немного драматично 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
    

    ResultSet:

    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
    

    ResultSet:

    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
    

    ResultSet:

    1
    2
    3
    4
    5
    

Но старайтесь избегать петель на уровне базы данных. Ссылка .


17
Здесь приведены те же примеры, вы являетесь автором этого сайта? blog.sqlauthority.com/2007/10/24/…
— анар халилов 06

1
Он не такой, но какое это имеет значение? Был дан правильный ответ. Ссылка на другой веб-сайт - это боль, копирование и вставка ответа здесь полезно.
— Джефф Грисвальд 02

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основе проще, потому что условие if не инвертируется. С другой стороны, он более подробный.

Если бы не все пренебрежение к использованию 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 "все еще применима.
— Джефф Грисвальд 02

1

Только цикл пока официально поддерживается SQL-сервером. Уже есть ответ на цикл DO while. Я подробно рассказываю о способах достижения различных типов циклов на SQL-сервере.

Если вы знаете, что вам все равно нужно завершить первую итерацию цикла, вы можете попробовать версию SQL-сервера DO..WHILE или REPEAT..UNTIL .

DO..WHILE цикл

DECLARE @X INT=1;

WAY:  --> Here the  DO statement

  PRINT @X;

  SET @X += 1;

IF @X<=10 GOTO WAY;

REPEAT..UNTIL Цикл

DECLARE @X INT = 1;

WAY:  -- Here the REPEAT statement

  PRINT @X;

  SET @X += 1;

IFNOT(@X > 10) GOTO WAY;

Цикл FOR

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: Оба ответа отвечают на разные вопросы. Дополнительные данные приведены в обоих ответах.
— Somnath
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.