Wykonaj pętlę while w programie SQL Server 2008

120

Czy istnieje metoda implementacji do whilepętli w SQL Server 2008?

Nithesh Narayanan
źródło
5
Odpowiedź udzielona przez Rahula jest prawidłowa, ale co dokładnie próbujesz osiągnąć? Pętle są drogie w porównaniu do rozwiązań opartych na zestawach. Być może uda się całkowicie uniknąć pętli.
Lieven Keersmaekers,
Nie używaj pętli, jeśli to w ogóle możliwe i szacuję, że w 95% lub więcej przypadków można ich uniknąć. Pętle i kursory zabijają wydajność i nigdy nie powinny być pisane przez nikogo, kto nie jest doświadczonym administratorem danych i ma co najmniej pięć lat dostrajania wydajności.
HLGEM
1
Er. nieco dramatyczny HLGEM, pętle i kursory są w rzeczywistości całkiem zgrabne, o ile nie przechodzisz przez każdy wiersz w tabeli. Jeśli masz listę kategorii, witryn lub coś stosunkowo wysokiego poziomu, pętla może być najskuteczniejszym sposobem wykonania zapytania.
Geoff Griswald

Odpowiedzi:

190

Nie jestem pewien co do DO-WHILE W MS SQL Server 2008, ale możesz zmienić logikę pętli WHILE, tak aby UŻYWAĆ jak pętla DO-WHILE.

Przykłady są pobierane stąd: http://blog.sqlauthority.com/2007/10/24/sql-server-simple-example-of-while-loop-with-continue-and-break-keywords/

  1. Przykład pętli WHILE

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

    Zestaw wyników:

    1
    2
    3
    4
    5
    
  2. Przykład pętli WHILE ze słowem kluczowym BREAK

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

    Zestaw wyników:

    1
    2
    3
    
  3. Przykład pętli WHILE ze słowami kluczowymi CONTINUE i 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
    

    Zestaw wyników:

    1
    2
    3
    4
    5
    

Ale staraj się unikać pętli na poziomie bazy danych. Odniesienie .

Pratik
źródło
17
Te same przykłady są podane tutaj, czy jesteś autorem tej witryny? blog.sqlauthority.com/2007/10/24/…
anar khalilov
1
Nie jest, ale dlaczego to ma znaczenie? Podano poprawną odpowiedź. Łączenie się z inną witryną jest uciążliwe, skopiowanie i wklejenie odpowiedzi tutaj jest pomocne.
Geoff Griswald
61

Jeśli GOTOsłowo kluczowe nie jest bardzo urażone , może zostać użyte do symulacji znaku DO/ WHILEw T-SQL. Rozważmy następujący, raczej bezsensowny przykład zapisany w pseudokodzie:

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

Oto odpowiednik kodu T-SQL przy użyciu goto:

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

Zwróć uwagę na mapowanie jeden do jednego między GOTOwłączonym rozwiązaniem a oryginalnym DO/ WHILEpseudokodem. Podobna implementacja wykorzystująca WHILEpętlę wyglądałaby następująco:

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

Teraz możesz oczywiście przepisać ten konkretny przykład jako prostą WHILEpętlę, ponieważ nie jest to dobry kandydat na konstrukcję DO/ WHILE. Nacisk położono na przykład raczej na zwięzłość niż możliwość zastosowania, ponieważ uzasadnione przypadki wymagające a DO/ WHILEsą rzadkie.


POWTARZAJ / DO KOGOŚ (NIE działa w T-SQL)?

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

... i GOTOrozwiązanie oparte na T-SQL:

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

Dzięki kreatywnemu użyciu GOTOi inwersji logiki za pomocą NOTsłowa kluczowego istnieje bardzo ścisły związek między oryginalnym pseudokodem a GOTOrozwiązaniem bazowym . Podobne rozwiązanie z wykorzystaniem WHILEpętli wygląda następująco:

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

Można argumentować, że w przypadku REPEAT/ UNTIL, WHILErozwiązanie bazowe jest prostsze, ponieważ warunek if nie jest odwrócony. Z drugiej strony jest też bardziej szczegółowy.

Gdyby nie cała pogarda związana z używaniem programu GOTO, mogłyby to być nawet idiomatyczne rozwiązania na te kilka razy, gdy te szczególne (złe) konstrukcje pętli są konieczne w kodzie T-SQL ze względu na przejrzystość.

Używaj ich według własnego uznania, starając się nie cierpieć gniewu innych programistów, gdy złapią cię na bardzo oczernianym GOTO.

Michael Goldshteyn
źródło
6
+1: zdecydowanie lepiej odpowiada na pytanie niż zaakceptowana odpowiedź.
Louis Kottmann,
Wolę podejście WHILE (1 = 1), ponieważ funkcjonalnie pasuje do celu bez użycia przerażającego GOTO.
kad81
Obie metody są prawidłowe. Podoba mi się pseudo-pętla używająca GOTO, jest całkiem sprytna.
Geoff Griswald
18

Wydaje mi się, że czytałem ten artykuł więcej niż raz, a odpowiedź jest tylko bliska tego, czego potrzebuję.

Zwykle, gdy myślę, że będę potrzebować DO WHILEw T-SQL, dzieje się tak, ponieważ iteruję kursor i szukam w dużej mierze optymalnej przejrzystości (w porównaniu z optymalną prędkością). W T-SQL wydaje się pasować do WHILE TRUE/ IF BREAK.

Jeśli to jest scenariusz, który Cię tu sprowadził, ten fragment może Ci zaoszczędzić chwilę. W przeciwnym razie witaj z powrotem, ja. Teraz mogę być pewien, że byłem tu więcej niż raz. :)

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

Niestety, T-SQL nie wydaje się oferować czystszego sposobu pojedynczego zdefiniowania operacji pętli niż ta nieskończona pętla.

Shannon
źródło
Korzystanie z kursora nigdy nie jest dobrą opcją, ponieważ wymaga znacznie więcej zasobów niż w rzeczywistości.
greektreat
10
@greektreat: Dzięki za głosy przeciwne :), ale jestem zdezorientowany! Jeśli „kursor nigdy nie jest dobrą opcją”, to zawsze musi być dobrą opcją, więc po co głosować przeciw? Poważnie jednak, oczywiście kursory mają wiele całkiem praktycznych zastosowań: przeciwko tabelom prywatnym, w przypadku małych operacji, w których jasność / zwięzłość> wydajność, w zadaniach konserwacyjnych, gdzie operacje deterministyczne nie są dostępne, dla niektórych operacji muszą być realizowane jako transakcje atomowe, niezależnie od tego itp. W moim ostatnim przypadku pozyskiwałem przychodzącą zmienną tabeli, prywatną do mojej procedury składowanej. Absolutny banał nigdy nie jest dobrym pomysłem!
shannon
5
@greektreat: podsumowując, czasami iteracja danych jest JEDYNĄ opcją. Przypuszczam, że nadal można argumentować, że w takim przypadku nie jest to dobra opcja, ale to nie znaczy, że ten rodzaj kodu jest niepotrzebny i wymaga negatywnej oceny.
shannon
1
Myślę, że w internecie jest wściekła armia ludzi, którzy są bardzo, bardzo wściekli na innych ludzi używających pętli i kursorów w SQL. Na tej stronie, jeśli nawet wspomnisz o używaniu pętli w SQL około 30 sekund później, twoja skrzynka odbiorcza zostanie zalana ignorantami, którzy powiedzą ci, abyś nie używał ich w ŻADNYCH okolicznościach ...
Geoff Griswald
4

Możesz także użyć zmiennej wyjścia, jeśli chcesz, aby Twój kod był nieco bardziej czytelny:

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

Byłoby to prawdopodobnie bardziej istotne, gdy masz bardziej skomplikowaną pętlę i próbujesz śledzić logikę. Jak wspomniano, pętle są drogie, więc jeśli możesz, spróbuj użyć innych metod.

Sebris87
źródło
To znaczy ... Żyjemy w czasach, w których nasze serwery baz danych mają od 10 do 20 rdzeni procesorów bezczynnych w dowolnym momencie i gdzie nasze kontrolery pamięci masowej mają dostępną przepustowość mierzoną w gigabitach, więc nie jestem pewien, czy jest to konwencjonalne ” mądrość ", że" LOOP = BAD "nadal obowiązuje.
Geoff Griswald
1

Tylko While Loop jest oficjalnie obsługiwany przez serwer SQL. Jest już odpowiedź na pętlę DO while. Szczegółowo opisuję sposoby uzyskiwania różnych typów pętli w serwerze SQL.

Jeśli wiesz, i tak musisz ukończyć pierwszą iterację pętli, możesz wypróbować wersję DO..WHILE lub REPEAT..UNTIL serwera SQL.

DO..WHILE Loop

DECLARE @X INT=1;

WAY:  --> Here the  DO statement

  PRINT @X;

  SET @X += 1;

IF @X<=10 GOTO WAY;

Pętla REPEAT..UNTIL

DECLARE @X INT = 1;

WAY:  -- Here the REPEAT statement

  PRINT @X;

  SET @X += 1;

IFNOT(@X > 10) GOTO WAY;

Dla pętli

DECLARE @cnt INT = 0;

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

PRINT 'Done FOR LOOP';

Odniesienie

Somnath Muluk
źródło
Wygląda to na zmianę kolejności kopiowania i wklejania ze stackoverflow.com/a/46362450/8239061 .
SecretAgentMan,
@SecretAgentMan: Obie odpowiedzi odpowiadają na różne pytania. Dodatkowe dane podane w obu odpowiedziach.
Somnath Muluk,