2009-01-22 38 views
22

El principal problema consiste en cambiar el índice de filas a 1,2,3 .. donde contact-id y type es el mismo. pero todas las columnas pueden contener exactamente los mismos datos debido a algún ex empleado desordenado y actualizar todas las filas por identificación de contacto y tipo. de alguna manera hay filas que no están en mal estado, pero las filas de índice son las mismas. Es un caos total.Cursor dentro del cursor

He intentado usar un cursor interno con las variables que provienen del cursor externo. Pero parece que está atrapado en el cursor interno.

Una parte de la consulta se parece a esto:

Fetch NEXT FROM OUTER_CURSOR INTO @CONTACT_ID, @TYPE 
While (@@FETCH_STATUS <> -1) 
BEGIN 
IF (@@FETCH_STATUS <> -2) 

    DECLARE INNER_CURSOR Cursor 
    FOR 
    SELECT * FROM CONTACTS 
    where CONTACT_ID = @CONTACT_ID 
    and TYPE = @TYPE 

    Open INNER_CURSOR 

    Fetch NEXT FROM INNER_CURSOR 
    While (@@FETCH_STATUS <> -1) 
    BEGIN 
    IF (@@FETCH_STATUS <> -2) 

Cuál puede ser el problema? ¿Es @@ FETCH_STATUS ambiguo o algo así?

EDIT: todo se ve bien si yo no uso este código dentro del cursor interno:

UPDATE CONTACTS 
SET INDEX_NO = @COUNTER 
where current of INNER_CURSOR 

EDIT: aquí está el panorama:

BEGIN TRAN 

DECLARE @CONTACT_ID VARCHAR(15) 
DECLARE @TYPE VARCHAR(15) 
DECLARE @INDEX_NO SMALLINT 
DECLARE @COUNTER SMALLINT 
DECLARE @FETCH_STATUS INT 

DECLARE OUTER_CURSOR CURSOR 

FOR 

SELECT CONTACT_ID, TYPE, INDEX_NO FROM CONTACTS 
WHERE 
CONTACT_ID IN (SELECT CONTACT_ID FROM dbo.CONTACTS 
WHERE CONTACT_ID IN(...) 
GROUP BY CONTACT_ID, TYPE, INDEX_NO 
HAVING COUNT(*) > 1 

OPEN OUTER_CURSOR 

FETCH NEXT FROM OUTER_CURSOR INTO @CONTACT_ID, @TYPE, @INDEX_NO 
WHILE (@@FETCH_STATUS <> -1) 
BEGIN 
IF (@@FETCH_STATUS <> -2) 

SET @COUNTER = 1 

     DECLARE INNER_CURSOR CURSOR 
     FOR 
     SELECT * FROM CONTACTS 
     WHERE CONTACT_ID = @CONTACT_ID 
     AND TYPE = @TYPE 
     FOR UPDATE 

     OPEN INNER_CURSOR 

     FETCH NEXT FROM INNER_CURSOR 

     WHILE (@@FETCH_STATUS <> -1) 
     BEGIN 
     IF (@@FETCH_STATUS <> -2) 

     UPDATE CONTACTS 
     SET INDEX_NO = @COUNTER 
     WHERE CURRENT OF INNER_CURSOR 

     SET @COUNTER = @COUNTER + 1 

     FETCH NEXT FROM INNER_CURSOR 
     END 
     CLOSE INNER_CURSOR 
     DEALLOCATE INNER_CURSOR 

FETCH NEXT FROM OUTER_CURSOR INTO @CONTACT_ID, @TYPE, @INDEX_NO 
END 
CLOSE OUTER_CURSOR 
DEALLOCATE OUTER_CURSOR 

COMMIT TRAN 
+0

Una pregunta más: qué versión del servidor sql, porque eso determinará qué podemos usar para crear los números de fila para reemplazar su @counter en el cursor interno. –

+0

¿Conoce la lógica basada en conjuntos? Los cursores se deben utilizar como último recurso ... –

+0

¿Puede ser tan amable como para ofrecer una respuesta? si necesita más información sobre la situación, con gusto la proporciono. –

Respuesta

-1

No entiendo completamente lo que era el problema con la "corriente de actualización de cursor" pero está resuelto mediante el uso de la instrucción fetch dos veces por el interior del cursor:

FETCH NEXT FROM INNER_CURSOR 

WHILE (@@FETCH_STATUS <> -1) 
BEGIN 

UPDATE CONTACTS 
SET INDEX_NO = @COUNTER 
WHERE CURRENT OF INNER_CURSOR 

SET @COUNTER = @COUNTER + 1 

FETCH NEXT FROM INNER_CURSOR 
FETCH NEXT FROM INNER_CURSOR 
END 
2

¿Haces alguna más recuperaciones? Deberías mostrar esos también. Solo nos muestras la mitad del código.

Debe quedar como:

FETCH NEXT FROM @Outer INTO ... 
WHILE @@FETCH_STATUS = 0 
BEGIN 
    DECLARE @Inner... 
    OPEN @Inner 
    FETCH NEXT FROM @Inner INTO ... 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
    ... 
    FETCH NEXT FROM @Inner INTO ... 
    END 
    CLOSE @Inner 
    DEALLOCATE @Inner 
    FETCH NEXT FROM @Outer INTO ... 
END 
CLOSE @Outer 
DEALLOCATE @Outer 

Además, asegúrese de que no nombra a los cursores lo mismo ... y cualquier código (ver sus disparadores) que es llamada no utiliza un cursor que se nombra lo mismo. He visto comportamientos extraños de personas que usan 'theCursor' en múltiples capas de la pila.

+0

el resto de la consulta se ve como lo describió. los nombres y las búsquedas del cursor se nombran correctamente. –

54

Tiene una variedad de problemas. Primero, ¿por qué estás usando tus valores específicos de @@ FETCH_STATUS? Simplemente debería ser @@ FETCH_STATUS = 0.

En segundo lugar, no está seleccionando su Cursor interno en nada. Y no puedo pensar en ninguna circunstancia en la que seleccionas todos los campos de esta manera: ¡deletéreos!

Aquí hay un ejemplo para seguir. La carpeta tiene una clave principal de "ClientID" que también es una clave foránea para Attend. Sólo estoy imprimiendo todos los UID asistir, desglosado por carpeta ClientID:

Declare @ClientID int; 
Declare @UID int; 

DECLARE Cur1 CURSOR FOR 
    SELECT ClientID From Folder; 

OPEN Cur1 
FETCH NEXT FROM Cur1 INTO @ClientID; 
WHILE @@FETCH_STATUS = 0 
BEGIN 
    PRINT 'Processing ClientID: ' + Cast(@ClientID as Varchar); 
    DECLARE Cur2 CURSOR FOR 
     SELECT UID FROM Attend Where [email protected]; 
    OPEN Cur2; 
    FETCH NEXT FROM Cur2 INTO @UID; 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
     PRINT 'Found UID: ' + Cast(@UID as Varchar); 
     FETCH NEXT FROM Cur2 INTO @UID; 
    END; 
    CLOSE Cur2; 
    DEALLOCATE Cur2; 
    FETCH NEXT FROM Cur1 INTO @ClientID; 
END; 
PRINT 'DONE'; 
CLOSE Cur1; 
DEALLOCATE Cur1; 

Por último, ¿está usted SEGURO desea estar haciendo algo como esto en un procedimiento almacenado? Es muy fácil abusar de los procedimientos almacenados y, a menudo, refleja problemas para caracterizar su problema. La muestra que di, por ejemplo, podría lograrse mucho más fácilmente usando llamadas de selección estándar.

+0

No selecciono nada porque simplemente quería actualizar la fila actual del cursor interno. ¿Tengo que usar en @somevariable? –

+0

¿Tiene más de un registro en su tabla de contactos para cada "Contact_ID"? Si es así, tu "Tener" es falso, siempre será falso. Si no, no importa. Solo intento descubrir tu lógica. Además, ¿no debería haber un cierre ")" después de la cláusula de tener? –

+0

Yikes: ¿está reasignando el campo INDEX_NO registro por registro, pero solo para aquellos registros en los que hay varios incluso después de tener en cuenta el antiguo INDEX_NO? –

8

También puede eludir los problemas del cursor anidado, problemas generales del cursor y problemas de variables globales al evitar los cursores por completo.

declare @rowid int 
declare @rowid2 int 
declare @id int 
declare @type varchar(10) 
declare @rows int 
declare @rows2 int 
declare @outer table (rowid int identity(1,1), id int, type varchar(100)) 
declare @inner table (rowid int identity(1,1), clientid int, whatever int) 

insert into @outer (id, type) 
Select id, type from sometable 

select @rows = count(1) from @outer 
while (@rows > 0) 
Begin 
    select top 1 @rowid = rowid, @id = id, @type = type 
    from @outer 
    insert into @innner (clientid, whatever) 
    select clientid whatever from contacts where contactid = @id 
    select @rows2 = count(1) from @inner 
    while (@rows2 > 0) 
    Begin 
     select top 1 /* stuff you want into some variables */ 
     /* Other statements you want to execute */ 
     delete from @inner where rowid = @rowid2 
     select @rows2 = count(1) from @inner 
    End 
    delete from @outer where rowid = @rowid 
    select @rows = count(1) from @outer 
End 
+0

¡Muchas gracias buen ejemplo! el problema es que tengo que usar la fila actual del cursor. porque pensar en 2 o más filas puede contener exactamente los mismos datos o datos diferentes que no se puede arriesgar a perder. problema con su esta solución es que es imposible hacer coincidir los datos con la tabla externa. –

2

Esto huele a algo que debería hacerse con un JOIN en su lugar. ¿Puedes compartir el problema más grande con nosotros?


Hey, yo debería ser capaz de conseguir esto a un solo estado, pero no he tenido tiempo para jugar con él más aún hoy en día y pueden no llegar.Mientras tanto, sepa que debe poder editar la consulta de su cursor interno para crear los números de fila como parte de la consulta utilizando la función ROW_NUMBER(). A partir de ahí, puede doblar el cursor interno en el exterior haciendo una UNIÓN INTERIOR en él (puede unirse a una consulta secundaria). Por último, cualquier instrucción SELECT se puede convertir en una actualización con este método:

UPDATE [YourTable/Alias] 
    SET [Column] = q.Value 
FROM 
(
    ... complicate select query here ... 
) q 

Dónde [YourTable/Alias] es una tabla o alias usado en la consulta de selección.

0

que tenía la misma problema,

lo que tiene que hacer es declarar el segundo cursor como: dECLARE [second_cursor] cursor local para

ves "pARA cursor local" en vez de "cursor para"

+0

LOCAL debe ser solo un modificador de alcance que limita la disponibilidad del cursor al alcance del procedimiento actual en lugar del alcance de conexión actual. No estoy seguro de cómo esto afectaría el problema anterior. – Milan