2012-09-04 23 views
10

En T-SQL, al iterar los resultados de un cursor, parece ser una práctica común repetir la instrucción FETCH antes del bucle WHILE. El siguiente ejemplo de Microsoft:Cómo evitar el FETCH duplicado en T-SQL cuando se usa un cursor?

DECLARE Employee_Cursor CURSOR FOR 
SELECT EmployeeID, Title FROM AdventureWorks2012.HumanResources.Employee 
    WHERE JobTitle = 'Marketing Specialist'; 
OPEN Employee_Cursor; 

FETCH NEXT FROM Employee_Cursor; 
WHILE @@FETCH_STATUS = 0 
    BEGIN 
     FETCH NEXT FROM Employee_Cursor; 
    END; 
CLOSE Employee_Cursor; 
DEALLOCATE Employee_Cursor; 
GO 

(. Observe cómo FETCH NEXT FROM Employee_Cursor; aparece dos veces)

Si el FETCH selecciona en una larga lista de variables, entonces tenemos una gran declaración duplicada, que es a la vez feo y, por supuesto, , código "no SECO".

No estoy al tanto de una declaración de T-SQL de control de flujo posterior a la condición por lo que parece que tendría que recurrir a un WHILE(TRUE) y luego BREAK cuando @@FETCH_STATUS no es cero. Esto me parece torpe.

¿Qué otras opciones tengo?

+2

En código que se muestra, Sustituir el primer 'FETCH DESPUÉS de Employee_Cursor' con' GOTO Employee_Cursor_Fetch' y lugar etiqueta 'Employee_Cursor_Fetch:' inmediatamente antes de permanecer 'FETCH NEXT'. Puede observar que el nombre de la etiqueta se deriva del nombre del cursor ('Employee_Cursor') para evitar algunas consideraciones sobre los nombres de las etiquetas. – miroxlav

Respuesta

-3

Simplemente dijimos que no se podía ... así es como funciona la mayoría de los enunciados en SQL. Debe obtener la primera línea antes del ciclo y luego hacerlo nuevamente en la instrucción while.

La mejor pregunta sobre cómo deshacerse del cursor y tratar de resolver su consulta sin él.

+1

Totalmente en desacuerdo. Puede evitar la duplicación, aunque quitar el cursor también es un objetivo valioso. –

+0

Si bien estoy de acuerdo en que en la mayoría de los casos se deben evitar los cursores en favor de las operaciones basadas en conjuntos, hay algunos casos en los que el cursor es absolutamente necesario. Es decir. Donde las filas deben evaluarse línea por línea y el resultado de las filas leídas previamente determinará el resultado de lecturas posteriores. – Nick

4

El primer Fetch no debe ser un Fetch next, solo un fetch.

Entonces no se repite.

me gustaría pasar más esfuerzo para deshacerse del cursor, y menos en el dogma SECO, (pero si lo que realmente importa, se puede utilizar un GOTO :) - Lo sentimos, M. Dijkstra)

GOTO Dry 
WHILE @@FETCH_STATUS = 0 
BEGIN 
    --- stuff here 

Dry: 
    FETCH NEXT FROM Employee_Cursor; 
END; 
+0

Estoy totalmente de acuerdo con la postura sobre los cursores. Tristemente, debo permitir que algunas filas fallen sin fallar toda la actualización.Hice que mi inserción en seleccionar todo listo y luego tuve que recurrir a los cursores para ignorar las conversiones anómalas en mi selección. Ho hum. –

+2

@BernhardHofmann ¿No puedes atrapar preventivamente las fallas con una cláusula WHERE? – podiluska

6

Esto es lo que he recurrido a (oh la vergüenza de él):

WHILE (1=1) 
BEGIN 
    FETCH NEXT FROM C1 INTO 
    @foo, 
    @bar, 
    @bufar, 
    @fubar, 
    @bah, 
    @fu, 
    @foobar, 
    @another, 
    @column, 
    @in, 
    @the, 
    @long, 
    @list, 
    @of, 
    @variables, 
    @used, 
    @to, 
    @retrieve, 
    @all, 
    @values, 
    @for, 
    @conversion 

    IF (@@FETCH_STATUS <> 0) 
    BEGIN 
     BREAK 
    END 

    -- Use the variables here 
END 

CLOSE C1 
DEALLOCATE C1 

Usted puede ver por qué he publicado una pregunta. No me gusta cómo se oculta el control del flujo en una declaración if cuando debería estar en el while.

+2

Creo que, técnicamente, esta debería ser la respuesta aceptada, ya que evita duplicar la instrucción 'FETCH'. – Jeroen

-2

Es obvio que un cursor es el puntero a la fila actual en el conjunto de registros. Pero el mero hecho de señalar no tendrá sentido a menos que se pueda usar. Aquí viene la declaración Fetch en la escena. Esto toma datos del conjunto de registros, los almacena en la (s) variable (s) proporcionada (s). por lo tanto, si elimina la primera instrucción de extracción, el ciclo while no funcionará, ya que no hay registro "FETCHED" para la manipulación, si elimina la última instrucción de extracción, el "while" no pasará por el bucle.

Por lo tanto, es necesario tener ambas declaraciones de búsqueda para recorrer el conjunto de registros completo.

+0

No es necesario (ver mi respuesta más arriba). Estaba pidiendo mejores formas de evitar escribir el control del flujo, no si necesito tener el alcance dos veces. –

8

Hay una buena estructura publicado en línea por Chris Oldwood lo que hace que sea muy elegante:

DECLARE @done bit = 0 

WHILE (@done = 0) 
BEGIN 
    -- Get the next author. 
    FETCH NEXT FROM authors_cursor 
    INTO @au_id, @au_fname, @au_lname 

    IF (@@FETCH_STATUS <> 0) 
    BEGIN 
    SET @done = 1 
    CONTINUE 
    END 

    -- 
    -- stuff done here with inner cursor elided 
    -- 
END 
Cuestiones relacionadas