Выполнить цикл 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
    

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

Pratik
источник
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 не предлагает более чистого способа одиночного определения операции цикла, чем этот бесконечный цикл.

Shannon
источник
Использование курсора никогда не бывает плохим вариантом, так как требует гораздо больше ресурсов, чем действительно требуется.
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

Это, вероятно, будет более уместно, если у вас более сложный цикл и вы пытаетесь отслеживать логику. Как указано, циклы дороги, поэтому попробуйте использовать другие методы, если можете.

Sebris87
источник
Я имею в виду ... Мы живем в эпоху, когда наши серверы баз данных имеют от 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