2009-06-08 17 views
5

Mi piacerebbe conoscere le proprie esperienze con la sostituzione dei cursori di SQL Server nel codice esistente, o come si è preso un problema che un utente procedurale avrebbe usato un cursore per risolvere, e lo ha fatto basato sul set.Quali sono i diversi modi per sostituire un cursore?

Qual è stato il problema per il quale è stato utilizzato il cursore? Come hai sostituito il cursore?

risposta

5

provare a non eseguire mai il ciclo, lavorare su insiemi di dati.

è possibile inserire, aggiornare, eliminare più righe contemporaneamente. qui in un esempio di più righe:

INSERT INTO YourTable 
     (col1, col2, col3, col4) 
    SELECT 
     cola, colb+Colz, colc, @X 
     FROM .... 
      LEFT OUTER JOIN ... 
     WHERE... 

Quando si guarda un ciclo, vedere cosa ha fatto al suo interno. Se sono solo inserimenti/cancelli/aggiornamenti, riscrivi per utilizzare singoli comandi. Se ci sono IF, vedere se questi possono essere dichiarazioni CASE o condizioni WHERE su inserti/cancella/aggiornamenti. In tal caso, rimuovere il loop e utilizzare i comandi set.

Ho preso i loop e li ho sostituiti con i comandi basati su set e ridotto il tempo di esecuzione da pochi minuti a pochi secondi. Ho preso le procedure con molti cicli nidificati e chiamate di procedura e mantenuto i loop (era impossibile usare solo insert/delete/updates), ma ho rimosso il cursore e ho visto meno blocchi/blocchi e massimi incrementi di prestazioni. Qui ci sono due metodi di loop che sono meglio di cicli con i cursori ...

se si deve loop, su un insieme fare qualcosa del genere:

--this looks up each row for every iteration 
DECLARE @msg VARCHAR(250) 
DECLARE @hostname sysname 

--first select of currsor free loop 
SELECT @hostname= min(RTRIM(hostname)) 
    FROM master.dbo.sysprocesses (NOLOCK) 
    WHERE hostname <> '' 

WHILE @hostname is not null 
BEGIN 
    set @msg='exec master.dbo.xp_cmdshell "net send ' 
     + RTRIM(@hostname) + ' ' 
     + 'testing "' 
    print @msg 
    --EXEC (@msg) 

    --next select of cursor free loop 
    SELECT @hostname= min(RTRIM(hostname)) 
     FROM master.dbo.sysprocesses (NOLOCK) 
     WHERE hostname <> '' 
     and hostname > @hostname 
END 

se si dispone di una serie ragionevole di articoli (non 100.000) per eseguire un ciclo su di voi può fare questo:

--this will capture each Key to loop over 
DECLARE @msg VARCHAR(250) 
DECLARE @From int 
DECLARE @To  int 
CREATE TABLE #Rows 
(
    RowID  int not null primary key identity(1,1) 
    ,hostname varchar(100) 
) 

INSERT INTO #Rows 
SELECT DISTINCT hostname 
    FROM master.dbo.sysprocesses (NOLOCK) 
    WHERE hostname <> '' 
SELECT @From=0,@[email protected]@ROWCOUNT 

WHILE @From<@To 
BEGIN 
    SET @[email protected]+1 

    SELECT @msg='exec master.dbo.xp_cmdshell "net send ' 
     + RTRIM(hostname) + ' ' 
     + 'testing "' 
     FROM #Rows WHERE [email protected] 
    print @msg 
    --EXEC (@msg) 
END 
2

Bene, spesso un app utilizzato per la programmazione procedurale - per abitudine - tenterà di fare tutto proceduralmente, anche in SQL.

Molto spesso, un SELECT con i parametri giusti potrebbe fare - o forse hai a che fare con un'istruzione UPDATE.

Il punto in realtà è: è necessario iniziare a pensare nelle operazioni di set e comunicare al proprio RDBMS ciò che si desidera fare, non come farlo passo dopo passo.

È difficile dare una singola risposta "giusta" a questo ... quasi bisognerebbe mostrarlo con un esempio concreto.

Marc

0

ho scritto un codice che calcola i totali di funzionamento per i dati finanziari relativi ad un determinato anno. In ogni trimestre, ho dovuto aggiungere il valore per il trimestre corrente al totale parziale mentre gestivo i valori NULL in modo tale che il totale parziale per il trimestre precedente fosse riportato quando il valore per il trimestre corrente era NULL.

Inizialmente, l'ho fatto utilizzando un cursore e dal punto di vista funzionale questo ha soddisfatto i requisiti aziendali. Da un punto di vista tecnico, si è rivelato un ostacolo allo spettacolo perché, man mano che la quantità di dati aumentava, il codice impiegava più tempo in modo esponenziale. La soluzione era sostituire il cursore con una sottoquery correlata che soddisfaceva i requisiti funzionali ed eliminava i problemi di prestazioni.

Spero che questo aiuti,

Bill

4

Ho sostituito alcuni cursori con i cicli While.

DECLARE @SomeTable TABLE 
(
    ID int IDENTITY (1, 1) PRIMARY KEY NOT NULL, 
    SomeNumber int, 
    SomeText varchar 
) 

DECLARE @theCount int 
DECLARE @theMax int 

DECLARE @theNumber int 
DECLARE @theText varchar 

INSERT INTO @SomeTable (SomeNumber, SomeText) 
SELECT Number, Text 
FROM PrimaryTable 

SET @theCount = 1 
SELECT @theMax = COUNT(ID) FROM @SomeTable 

WHILE (@theCount <= @theMax) 
BEGIN 

    SET @theNumber = 0 
    SET @theText = '' 

    SELECT @theNumber = IsNull(Number, 0), @theText = IsNull(Text, 'nothing') 
    FROM @SomeTable 
    WHERE ID = @theCount 

    -- Do something. 
    PRINT 'This is ' + @theText + ' from record ' + CAST(@theNumber AS varchar) + '.' 

    SET @theCount = @theCount + 1 

END 

PRINT 'Done' 
Problemi correlati