web-dev-qa-db-fra.com

Curseur à l'intérieur du curseur

Le problème principal concerne la modification de l'index des lignes en 1,2,3 .. où id-contact et type sont identiques. mais toutes les colonnes peuvent contenir exactement les mêmes données à cause d'un ancien employé embêté et mettre à jour toutes les lignes par contact-id et type. d'une manière ou d'une autre, certaines lignes ne sont pas altérées, mais les lignes d'index sont identiques. C'est le chaos total. 

J'ai essayé d'utiliser un curseur interne avec les variables provenant du curseur externe. Mais il semble que son coincé dans le curseur intérieur.

Une partie de la requête ressemble à ceci:

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)

Quel peut être le problème? Est-ce que @@ FETCH_STATUS est ambigu ou quelque chose?

EDIT: tout va bien si je n'utilise pas ce code à l'intérieur du curseur intérieur:

UPDATE CONTACTS
SET INDEX_NO = @COUNTER
where current of INNER_CURSOR

EDIT: voici la grande image:

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
25
Orkun Balkancı

Vous avez une variété de problèmes. Premièrement, pourquoi utilisez-vous vos valeurs spécifiques @@ FETCH_STATUS? Cela devrait simplement être @@ FETCH_STATUS = 0.

Deuxièmement, vous ne sélectionnez pas votre curseur intérieur en rien. Et je ne peux penser à aucune circonstance dans laquelle vous sélectionneriez tous les champs de cette manière - précisez-les!

Voici un exemple à suivre. Le dossier a une clé primaire "ClientID" qui est également une clé étrangère pour Attend. J'imprime juste tous les UID de participation, classés par dossier 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 ClientID=@ClientID;
    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;

Enfin, êtes-vous S&UCIRC;R que vous souhaitez utiliser dans une procédure stockée? Il est très facile d’abuser des procédures stockées et reflète souvent des problèmes pour caractériser votre problème. L’échantillon que j’ai donné, par exemple, pourrait être beaucoup plus facile à utiliser avec des appels standard sélectionnés.

57
Mark Brittingham

Vous pouvez également éviter les problèmes de curseur imbriqués, les problèmes de curseur généraux et les problèmes de variable globale en évitant complètement les curseurs. 

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
9
cmsjr

Est-ce que vous faites plus de récupérations? Vous devriez les montrer aussi. Vous ne nous montrez que la moitié du code.

Cela devrait ressembler à:

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

Assurez-vous également que les curseurs ne sont pas nommés de la même façon ... et que tout code appelé (vérifiez vos déclencheurs) appelé n’utilise pas de curseur portant le même nom. J'ai vu un comportement étrange de la part de personnes utilisant 'theCursor' dans plusieurs couches de la pile.

2
Amy B

Cela sent quelque chose qui devrait être fait avec un JOIN à la place. Pouvez-vous partager le plus gros problème avec nous?


Hé, je devrais être capable de résumer cela à une seule déclaration, mais je n’ai pas encore eu le temps de jouer avec elle plus loin aujourd’hui et je n’aurais peut-être pas la parole. En attendant, sachez que vous devriez pouvoir modifier la requête de votre curseur interne pour créer les numéros de ligne dans le cadre de la requête à l'aide de la fonction ROW_NUMBER () . À partir de là, vous pouvez replier le curseur interne vers l’extérieur en faisant un INNER JOIN dessus (vous pouvez rejoindre une sous-requête). Enfin, toute instruction SELECT peut être convertie en UPDATE en utilisant cette méthode:

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

[YourTable/Alias] est une table ou un alias utilisé dans la requête select. 

2
Joel Coehoorn

J'ai eu le même problème,

ce que vous devez faire est de déclarer le second curseur comme suit: DECLARE [second_cursor] Cursor LOCAL For

Vous voyez "CURSEUR LOCAL POUR" au lieu de "CURSEUR POUR"

0
Julian Salinas