Faire une boucle while dans SQL Server 2008

120

Existe-t-il une méthode pour implémenter la do whileboucle dans SQL Server 2008?

Nithesh Narayanan
la source
5
La réponse donnée par Rahul est correcte mais qu'essayez-vous de réaliser exactement? Les boucles sont chères par rapport aux solutions basées sur des ensembles. Il est peut-être possible d'éviter complètement une boucle.
Lieven Keersmaekers
N'utilisez pas de boucles si possible et j'estimerais que 95% du temps ou plus, il est possible de les éviter. Les boucles et les curseurs sont des tueurs de performances et ne doivent jamais être écrits par une personne qui n'est pas un DBA expérimenté avec au moins cinq ans de réglage des performances.
HLGEM
1
Euh. légèrement dramatique là HLGEM, les boucles et les curseurs sont en fait assez soignés tant que vous ne parcourez pas chaque ligne d'une table. Si vous avez une liste de catégories ou de sites ou quelque chose de relativement haut niveau, une boucle peut bien être le moyen le plus efficace d'exécuter votre requête.
Geoff Griswald

Réponses:

190

Je ne suis pas sûr de DO-WHILE DANS MS SQL Server 2008 mais vous pouvez changer votre logique de boucle WHILE, de manière à utiliser comme une boucle DO-WHILE.

Des exemples sont tirés d'ici: http://blog.sqlauthority.com/2007/10/24/sql-server-simple-example-of- While - loop - with - continue- and- break - keywords/

  1. Exemple de boucle 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. Exemple de boucle WHILE avec le mot clé 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. Exemple de boucle WHILE avec les mots clés CONTINUE et 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
    

Mais essayez d' éviter les boucles au niveau de la base de données. Référence .

Pratik
la source
17
Les mêmes exemples sont donnés ici, êtes-vous l'auteur de ce site? blog.sqlauthority.com/2007/10/24/…
anar khalilov
1
Il ne l'est pas, mais pourquoi est-ce important? La bonne réponse a été donnée. Créer un lien vers un autre site Web est une douleur, copier et coller la réponse ici est utile.
Geoff Griswald
61

Si vous n'êtes pas très offensé par le GOTOmot - clé, il peut être utilisé pour simuler un DO/ WHILEdans T-SQL. Prenons l'exemple suivant, plutôt absurde, écrit en pseudocode:

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

Voici le code T-SQL équivalent utilisant goto:

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

Notez le mappage un à un entre la GOTOsolution activée et le DO/ WHILEpseudo - code d'origine. Une implémentation similaire utilisant une WHILEboucle ressemblerait à ceci:

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

Maintenant, vous pouvez bien sûr réécrire cet exemple particulier comme une simple WHILEboucle, car ce n'est pas un si bon candidat pour une construction DO/ WHILE. L'accent était mis sur la brièveté de l'exemple plutôt que sur l'applicabilité, car les cas légitimes nécessitant un DO/ WHILEsont rares.


REPEAT / JUSQU'À, n'importe qui (ne fonctionne PAS en T-SQL)?

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

... et la GOTOsolution basée sur T-SQL:

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

Grâce à l'utilisation créative GOTOet à l'inversion de logique via le NOTmot - clé, il existe une relation très étroite entre le pseudo-code d'origine et la GOTOsolution basée. Une solution similaire utilisant une WHILEboucle ressemble à ceci:

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

Un argument peut être fait que pour le cas du REPEAT/ UNTIL, la WHILEsolution basée est plus simple, car la condition if n'est pas inversée. D'un autre côté, il est également plus verbeux.

S'il n'y avait pas tout le mépris autour de l'utilisation de GOTO, il pourrait même s'agir de solutions idiomatiques pour les rares fois où ces constructions de boucle (mal) particulières sont nécessaires dans le code T-SQL pour des raisons de clarté.

Utilisez-les à votre propre discrétion, en essayant de ne pas subir la colère de vos collègues développeurs lorsqu'ils vous surprennent à utiliser le très décrié GOTO.

Michael Goldshteyn
la source
6
+1: répond définitivement mieux à la question que la réponse acceptée.
Louis Kottmann
Je préfère l'approche WHILE (1 = 1), car elle correspond fonctionnellement à l'objectif sans utiliser le redouté GOTO.
kad81
Les deux méthodes sont valides. J'aime la pseudo-boucle utilisant GOTO, c'est assez malin.
Geoff Griswald
18

Il me semble me souvenir d'avoir lu cet article plus d'une fois, et la réponse n'est que proche de ce dont j'ai besoin.

Habituellement, quand je pense que je vais avoir besoin d'un DO WHILEen T-SQL, c'est parce que j'itère un curseur et que je recherche en grande partie une clarté optimale (par rapport à une vitesse optimale). Dans T-SQL, cela semble correspondre à un WHILE TRUE/ IF BREAK.

Si c'est le scénario qui vous a amené ici, cet extrait de code peut vous faire gagner un instant. Sinon, bon retour, moi. Maintenant, je peux être certain que je suis venu ici plus d'une fois. :)

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

Malheureusement, T-SQL ne semble pas offrir un moyen plus propre de définir individuellement l'opération de boucle que cette boucle infinie.

Shannon
la source
L'utilisation d'un curseur n'est jamais une bonne option car cela prend beaucoup plus de ressources que ce qui est réellement nécessaire.
greektreat
10
@greektreat: Merci pour le vote défavorable :), mais je suis confus! Si "un curseur n'est jamais une bonne option", alors il doit toujours être une bonne option, alors pourquoi voter contre? Sérieusement, cependant, les curseurs ont évidemment de nombreuses utilisations assez pratiques: contre les tables privées, pour les petites opérations où clarté / brièveté> performances, pour les tâches de maintenance, où les opérations déterministes ne sont pas disponibles, car certaines opérations doivent se produire comme une transaction atomique quoi qu'il en soit, etc. Dans mon cas récent, j'étais en train d'ingérer une variable de table entrante, privée de ma procédure stockée. Une platitude absolue n'est jamais une bonne idée!
shannon
5
@greektreat: en résumé, l'itération des données est parfois la SEULE option. Je suppose que vous pourriez encore soutenir que ce n'est pas une bonne option dans ce cas, mais cela ne signifie pas que ce type de code est inutile et nécessite un vote défavorable.
shannon
1
Je pense qu'il y a une armée enragée de personnes sur Internet qui sont très, très en colère contre d'autres personnes utilisant des boucles et des curseurs en SQL. Sur ce site Web, si vous mentionnez même l'utilisation d'une boucle en SQL environ 30 secondes plus tard, votre boîte de réception sera inondée de personnes ignorantes vous disant de ne pas les utiliser dans toutes les circonstances ...
Geoff Griswald
4

Vous pouvez également utiliser une variable de sortie si vous souhaitez que votre code soit un peu plus lisible:

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

Ce serait probablement plus pertinent lorsque vous avez une boucle plus compliquée et que vous essayez de garder une trace de la logique. Comme indiqué, les boucles sont chères, alors essayez d'utiliser d'autres méthodes si vous le pouvez.

Sebris87
la source
Je veux dire ... Nous vivons à une époque où nos serveurs de base de données ont entre 10 et 20 cœurs de processeur inactifs à un moment donné, et où nos contrôleurs de stockage ont leur bande passante disponible mesurée en Gigabits, donc je ne suis pas sûr que ce soit conventionnel " la sagesse "que" LOOP = BAD "s'applique toujours.
Geoff Griswald
1

Only While Loop est officiellement pris en charge par le serveur SQL. Il y a déjà une réponse pour DO while loop. Je détaille la réponse sur les moyens de réaliser différents types de boucles dans le serveur SQL.

Si vous savez, vous devez quand même terminer la première itération de la boucle, alors vous pouvez essayer la version DO..WHILE ou REPEAT..UNTIL du serveur SQL.

Boucle DO..WHILE

DECLARE @X INT=1;

WAY:  --> Here the  DO statement

  PRINT @X;

  SET @X += 1;

IF @X<=10 GOTO WAY;

Boucle REPEAT..UNTIL

DECLARE @X INT = 1;

WAY:  -- Here the REPEAT statement

  PRINT @X;

  SET @X += 1;

IFNOT(@X > 10) GOTO WAY;

Boucle FOR

DECLARE @cnt INT = 0;

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

PRINT 'Done FOR LOOP';

Référence

Somnath Muluk
la source
Cela ressemble à un copier-coller-réorganiser depuis stackoverflow.com/a/46362450/8239061 .
SecretAgentMan
@SecretAgentMan: Les deux réponses répondent à des questions différentes. Données supplémentaires fournies dans les deux réponses.
Somnath Muluk