Hacer while loop en SQL Server 2008

120

¿Existe algún método para implementar el do whilebucle en SQL Server 2008?

Nithesh Narayanan
fuente
5
La respuesta dada por Rahul es correcta, pero ¿qué estás tratando de lograr exactamente? Los bucles son costosos en comparación con las soluciones basadas en conjuntos. Quizás sea posible evitar un bucle por completo.
Lieven Keersmaekers
No use bucles si es posible y yo estimaría que el 95% de las veces o más es posible evitarlos. Los bucles y cursores son asesinos del rendimiento y nunca deben ser escritos por nadie que no sea un DBA experimentado con al menos cinco años de ajuste de rendimiento.
HLGEM
1
Er. ligeramente dramáticos allí HLGEM, los bucles y los cursores son realmente bastante prolijos siempre que no esté recorriendo cada fila de una tabla. Si tiene una lista de categorías o sitios o algo de nivel relativamente alto, entonces un bucle puede ser la forma más eficiente de ejecutar su consulta.
Geoff Griswald

Respuestas:

190

No estoy seguro acerca de DO-WHILE IN MS SQL Server 2008, pero puede cambiar su lógica de bucle WHILE, para usar como bucle DO-WHILE.

Los ejemplos se toman de aquí: http://blog.sqlauthority.com/2007/10/24/sql-server-simple-example-of- while - loop - with - continue - and - break - keywords/

  1. Ejemplo de bucle WHILE

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

    Conjunto resultante:

    1
    2
    3
    4
    5
    
  2. Ejemplo de bucle WHILE con la palabra clave BREAK

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

    Conjunto resultante:

    1
    2
    3
    
  3. Ejemplo de bucle WHILE con palabras clave CONTINUE y 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
    

    Conjunto resultante:

    1
    2
    3
    4
    5
    

Pero intente evitar bucles a nivel de base de datos. Referencia .

Pratik
fuente
17
Aquí se dan los mismos ejemplos, ¿es usted el autor de este sitio web? blog.sqlauthority.com/2007/10/24/…
anar khalilov
1
No lo es, pero ¿por qué importa? Se dio la respuesta correcta. Vincular a otro sitio web es complicado, copiar y pegar la respuesta aquí es útil.
Geoff Griswald
61

Si no se siente muy ofendido por la GOTOpalabra clave, se puede utilizar para simular un DO/ WHILEen T-SQL. Considere el siguiente ejemplo bastante absurdo escrito en pseudocódigo:

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

Aquí está el código T-SQL equivalente usando goto:

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

Nótese el que uno entre la GOTOsolución activada y el original DO/ WHILEpseudocódigo. Una implementación similar usando un WHILEbucle se vería así:

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

Ahora, por supuesto, podría reescribir este ejemplo en particular como un WHILEbucle simple , ya que no es un buen candidato para una construcción DO/ WHILE. Se hizo hincapié en la brevedad del ejemplo más que en la aplicabilidad, ya que los casos legítimos que requieren una DO/ WHILEson raros.


REPETIR / HASTA, alguien (NO funciona en T-SQL)?

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

... y la GOTOsolución basada en T-SQL:

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

Mediante el uso creativo GOTOy la inversión lógica a través de la NOTpalabra clave, existe una relación muy estrecha entre el pseudocódigo original y la GOTOsolución basada. Una solución similar que usa un WHILEbucle se ve así:

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

Se puede argumentar que para el caso de REPEAT/ UNTIL, la WHILEsolución basada es más simple, porque la condición if no está invertida. Por otro lado, también es más detallado.

Si no fuera por todo el desdén en torno al uso de GOTO, estas incluso podrían ser soluciones idiomáticas para esas pocas ocasiones en las que estas construcciones de bucle (malvadas) en particular son necesarias en el código T-SQL en aras de la claridad.

Úselos a su propia discreción, tratando de no sufrir la ira de sus compañeros desarrolladores cuando lo atrapen usando el tan difamado GOTO.

Michael Goldshteyn
fuente
6
+1: definitivamente responde la pregunta mejor que la respuesta aceptada.
Louis Kottmann
Prefiero el enfoque MIENTRAS (1 = 1), ya que se ajusta al propósito funcionalmente sin usar el temido GOTO.
kad81
Ambos métodos son válidos. Me gusta el pseudo-bucle usando GOTO, es bastante inteligente.
Geoff Griswald
18

Me parece recordar haber leído este artículo más de una vez, y la respuesta solo se acerca a lo que necesito.

Por lo general, cuando creo que voy a necesitar un DO WHILET-SQL es porque estoy iterando un cursor y estoy buscando en gran medida una claridad óptima (frente a la velocidad óptima). En T-SQL, eso parece ajustarse a WHILE TRUE/ IF BREAK.

Si ese es el escenario que lo trajo aquí, este fragmento puede ahorrarle un momento. De lo contrario, bienvenido de nuevo, yo. Ahora puedo estar seguro de que he estado aquí más de una vez. :)

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

Desafortunadamente, T-SQL no parece ofrecer una forma más limpia de definir individualmente la operación de bucle, que este bucle infinito.

Shannon
fuente
Usar un cursor nunca no es una buena opción, ya que requiere muchos más recursos de los que realmente se requieren.
greektreat
10
@greektreat: Gracias por el voto negativo :), ¡pero estoy confundido! Si "un cursor nunca no es una buena opción", entonces siempre debe ser una buena opción, entonces, ¿por qué el voto negativo? En serio, sin embargo, obviamente los cursores tienen muchos usos bastante prácticos: contra tablas privadas, para operaciones pequeñas donde claridad / brevedad> rendimiento, para tareas de mantenimiento, donde las operaciones deterministas no están disponibles, para ciertas operaciones deben ocurrir como una transacción atómica independientemente, etc. En mi caso reciente, estaba ingiriendo una variable de tabla entrante, privada para mi procedimiento almacenado. ¡Un tópico absoluto nunca es una buena idea!
shannon
5
@greektreat: en resumen, a veces la iteración de datos es la ÚNICA opción. Supongo que aún podría argumentar que no es una buena opción en ese caso, pero eso no significa que este tipo de código sea innecesario y necesite una votación negativa.
Shannon
1
Creo que hay un ejército rabioso de personas en Internet que están muy, muy enojadas porque otras personas usan bucles y cursores en SQL. En este sitio web, si siquiera menciona el uso de un bucle en SQL unos 30 segundos después, su bandeja de entrada se inundará con personas ignorantes que le dirán que no los use bajo CUALQUIER circunstancia ...
Geoff Griswald
4

También puede usar una variable de salida si desea que su código sea un poco más legible:

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

Esto probablemente sería más relevante cuando tiene un ciclo más complicado y está tratando de realizar un seguimiento de la lógica. Como se indicó, los bucles son caros, así que intente utilizar otros métodos si puede.

Sebris87
fuente
Quiero decir ... Vivimos en una época en la que nuestros servidores de bases de datos tienen entre 10 y 20 núcleos de CPU inactivos en un momento dado, y donde nuestros controladores de almacenamiento tienen su ancho de banda disponible medido en Gigabits, así que no estoy seguro de que sea convencional ". sabiduría "que" LOOP = BAD "todavía se aplica.
Geoff Griswald
1

Only While Loop es oficialmente compatible con SQL Server. Ya hay una respuesta para DO while loop. Estoy detallando la respuesta sobre las formas de lograr diferentes tipos de bucles en el servidor SQL.

Si lo sabe, debe completar la primera iteración del ciclo de todos modos, entonces puede probar DO..WHILE o REPEAT..UNTIL versión del servidor SQL.

DO..WHILE Loop

DECLARE @X INT=1;

WAY:  --> Here the  DO statement

  PRINT @X;

  SET @X += 1;

IF @X<=10 GOTO WAY;

REPETIR ... HASTA bucle

DECLARE @X INT = 1;

WAY:  -- Here the REPEAT statement

  PRINT @X;

  SET @X += 1;

IFNOT(@X > 10) GOTO WAY;

En bucle

DECLARE @cnt INT = 0;

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

PRINT 'Done FOR LOOP';

Referencia

Somnath Muluk
fuente
Esto parece un copiar-pegar-reordenar de stackoverflow.com/a/46362450/8239061 .
SecretAgentMan
@SecretAgentMan: Ambas respuestas responden preguntas diferentes. Datos adicionales proporcionados en ambas respuestas.
Somnath Muluk