2009-01-22 18 views
22

Il problema principale riguarda la modifica dell'indice di righe su 1,2,3 .. dove ID di contatto e tipo sono uguali. ma tutte le colonne possono contenere esattamente gli stessi dati a causa di alcuni ex dipendenti incasinati e aggiornare tutte le righe per nome-contatto e tipo. in qualche modo ci sono righe che non sono disordinate ma le file di indice sono uguali. È il caos totale.Cursore all'interno del cursore

Ho provato a utilizzare un cursore interno con le variabili provenienti dal cursore esterno. Ma sembra che sia bloccato nel cursore interno.

Una parte della query si presenta così:

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) 

Quale può essere il problema? @@ FETCH_STATUS è ambiguo o qualcosa del genere?

EDIT: sembra tutto bene, se io non uso questo codice all'interno cursore interno:

UPDATE CONTACTS 
SET INDEX_NO = @COUNTER 
where current of INNER_CURSOR 

EDIT: ecco il quadro generale:

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

Un'altra domanda: quale versione di SQL Server, perché determinerà cosa possiamo usare per creare i numeri di riga per sostituire @counter nel cursore interno. –

+0

Sei a conoscenza della logica basata su set? I cursori dovrebbero essere usati come ultima risorsa ... –

+0

Quindi, puoi essere così gentile da offrire una risposta? se hai bisogno di maggiori informazioni sulla situazione, gentilmente me lo fornisco volentieri. –

risposta

-1

non capisco pienamente quello che era il problema con la "corrente aggiornamento cursore" ma è risolto utilizzando l'istruzione prendere due volte per il cursore interno:

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

Ti fare di più recupera? Dovresti mostrare anche quelli. Ci stai mostrando solo metà del codice.

Esso dovrebbe essere simile:

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 

Inoltre, assicurarsi che non chiami i cursori la stessa ... e tutto il codice (controllare i trigger) che viene chiamato non utilizzare un cursore che è denominato lo stesso. Ho visto strani comportamenti da persone che usano "theCursor" in più livelli dello stack.

+0

il resto della query appare esattamente come hai descritto. i nomi dei cursori e i recuperi sono correttamente denominati. –

54

Hai una varietà di problemi. Innanzitutto, perché stai utilizzando i tuoi valori specifici @@ FETCH_STATUS? Dovrebbe essere solo @@ FETCH_STATUS = 0.

In secondo luogo, non si seleziona il Cursore interno nel numero. E non riesco a pensare a nessuna circostanza in cui selezioni tutti i campi in questo modo - spiegali!

Ecco un esempio da seguire. La cartella ha una chiave primaria di "ClientID" che è anche una chiave esterna per Attend. Sto solo la stampa di tutti i UID Frequenta, suddivisi per cartella 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; 

Infine, sei SICURO si vuole fare qualcosa di simile in una stored procedure? È molto facile abusare delle stored procedure e spesso riflette problemi nel caratterizzare il tuo problema. Il campione che ho dato, ad esempio, potrebbe essere molto più facilmente realizzato utilizzando le chiamate di selezione standard.

+0

Non selezionare nulla perché volevo semplicemente aggiornare la riga corrente del cursore interno. devo usare in @somevariable? –

+0

Hai più di un record nella tabella dei contatti per ogni "ID_di_di_di_telefono"? Se è così, il tuo "avere" è spurio, sarà sempre falso. Se no, non importa. Sto solo cercando di capire la tua logica. Inoltre, non dovrebbe esserci una chiusura ")" dopo la clausola di avere? –

+0

Yikes - ti stai riassegnando al campo INDEX_NO su base record per record ma solo per quei record in cui ci sono multipli anche dopo aver tenuto conto del vecchio INDEX_NO? –

8

È inoltre possibile evitare problemi di cursore nidificati, problemi generali del cursore e problemi di variabili globali evitando completamente i cursori.

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

Grazie mille, bel esempio! il problema è che devo usare una riga di cursore corrente. perché pensare a 2 o più righe può contenere esattamente gli stessi dati o dati diversi che non si può rischiare di perdere. problema con questa soluzione è impossibile abbinare i dati con una tabella esterna. –

2

Questo odore di qualcosa che dovrebbe essere fatto con un JOIN invece. Puoi condividere il problema più grande con noi?


Ehi, dovrei essere in grado di ottenere questo fino a una singola istruzione, ma non ho avuto il tempo di giocare con lui ulteriormente ancora oggi e non può raggiungere.Nel frattempo, sappi che dovresti essere in grado di modificare la query per il cursore interno per creare i numeri di riga come parte della query utilizzando la funzione ROW_NUMBER(). Da lì, puoi piegare il cursore interno all'esterno eseguendo un JOIN INTERNO su di esso (puoi partecipare a una query secondaria). Infine, qualsiasi istruzione SELECT può essere convertito in un aggiornamento utilizzando questo metodo:

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

Dove [YourTable/Alias] è una tabella o alias utilizzato nella query di selezione.

0

Ho avuto lo stesso problema,

quello che dovete fare è dichiarare il secondo cursore come: DECLARE [second_cursor] cursore locale per

vedete "cURSORE LOCALE pER" invece di "del cursore per"

+0

LOCAL dovrebbe essere solo un modificatore di ambito che limita la disponibilità del cursore all'ambito della procedura corrente anziché all'ambito della connessione corrente. Non sei sicuro di come questo influirà sul problema precedente. – Milan