Domanda

Diciamo che ho un trigger come questo:

CREATE TRIGGER trigger1
   ON [dbo].[table1] 
   AFTER UPDATE
AS 
BEGIN               
    --declare some vars
    DECLARE @Col1 SMALLINT 
    DECLARE @Col1 TINYINT 

    --declare cursor        
    DECLARE Cursor1 CURSOR FOR 
    SELECT Col1, Col2 FROM INSERTED             

    --do the job
    OPEN Cursor1
    FETCH NEXT FROM Cursor1 INTO @Col1, @Col2

    WHILE @@FETCH_STATUS = 0
    BEGIN
        IF ...something...
        BEGIN           
            EXEC myProc1 @param1 = @Col1, @Param2 = @Col2
        END             
        ELSE
        IF ...something else...
        BEGIN           
            EXEC myProc2 @param1 = @Col1, @Param2 = @Col2
        END     

        FETCH NEXT FROM Cursor1 INTO @Col1, @Col2               
    END

    --clean it up       
    CLOSE Cursor1
    DEALLOCATE Cursor1                  
END

Voglio essere sicuro che Cursor1 sia sempre chiuso e deallocato. Anche myProc1 o myProc2 falliscono.

Devo usare il blocco try / catch?

È stato utile?

Soluzione

Sì, usa TRY / CATCH ma assicurati di deallocare ecc. dopo. Sfortunatamente, non c'è finalmente in SQL Server.

Tuttavia, suggerisco di racchiuderlo in un altro tentativo / cattura

CREATE TRIGGER trigger1 ON [dbo].[table1] AFTER UPDATE
AS 
BEGIN                           
    --declare some vars
    DECLARE @Col1 SMALLINT, @Col1 TINYINT 

    BEGIN TRY
        --declare cursor            
        DECLARE Cursor1 CURSOR FOR 
        SELECT Col1, Col2 FROM INSERTED                     

        --do the job
        OPEN Cursor1
        FETCH NEXT FROM Cursor1 INTO @Col1, @Col2

        WHILE @@FETCH_STATUS = 0
        BEGIN
            IF ...something...
                    EXEC myProc1 @param1 = @Col1, @Param2 = @Col2
            ELSE
            IF ...something else...
                    EXEC myProc2 @param1 = @Col1, @Param2 = @Col2

            FETCH NEXT FROM Cursor1 INTO @Col1, @Col2                               
        END
    END TRY
    BEGIN CATCH
        --do what you have to
    END CATCH

    BEGIN TRY
        --clean it up               
        CLOSE Cursor1
        DEALLOCATE Cursor1                                  
    END TRY
    BEGIN CATCH
        --do nothing
    END CATCH
END

Il fatto che un cursore in un trigger sia una buona idea è una questione diversa ...

Altri suggerimenti

È possibile utilizzare la funzione CURSOR_STATUS ().

if CURSOR_STATUS('global','cursor_name') >= 0 
begin
 close cursor_name
  deallocate cursor_name 
end

riferimento : http://msdn.microsoft .com / it-it / library / ms177609.aspx

Quello che dovresti fare non è mai usare un cursore in un trigger. Scrivi invece il codice corretto basato su set. Se qualcuno eseguisse un'importazione di dati nella tua tabella di 100.000 nuovi record, blocceresti la tabella per ore e porteresti il ??tuo database a una battuta d'arresto. È una pratica molto scadente utilizzare un cursore in un trigger.

Dieci anni dopo, immagino che dovrei aggiungere alcune informazioni a questa particolare domanda.

Esistono due soluzioni principali al tuo problema. Innanzitutto, utilizzare una dichiarazione del cursore LOCAL :

DECLARE --Operation
    Cursor1 -- Name
CURSOR -- Type
    LOCAL READ_ONLY FORWARD_ONLY -- Modifiers
FOR -- Specify Iterations
SELECT Col1, Col2 FROM INSERTED;

Questo limita il tuo particolare cursore solo alla sessione attiva, piuttosto che al contesto globale del server, supponendo che nessun'altra azione stia chiamando in questo cursore. Simile in linea di principio è usare una variabile cursore, che apparirebbe così:

DECLARE @Cursor1 CURSOR;
SET @Cursor1 = CURSOR LOCAL READ_ONLY FORWARD_ONLY FOR SELECT Col1, Col2 FROM INSERTED;

Usando una variabile cursore, puoi sempre sovrascriverla in qualsiasi momento usando la sintassi SET , oltre a gestire l'ambito in modo che rientri nella tua sessione particolare come nell'esempio precedente. Sovrascrivendo il contesto del cursore, si dealloca in modo efficace qualsiasi riferimento passato che aveva. Detto questo, entrambi questi approcci realizzano la tua intenzione originale collegando lo stato del cursore all'attività della connessione corrente.

Ciò potrebbe lasciare un blocco persistente se il contesto dell'app utilizza Pool di connessioni, nel qual caso dovresti utilizzare il modello Try-Catch come segue:

CREATE TRIGGER trigger1
   ON [dbo].[table1] 
   AFTER UPDATE
AS 
BEGIN               
    --declare some vars
    DECLARE @Col1 SMALLINT;
    DECLARE @Col2 TINYINT;

    --declare cursor        
    DECLARE 
        Cursor1 
    CURSOR 
        LOCAL READ_ONLY FORWARD_ONLY 
    FOR 
        SELECT 
            Col1, 
            Col2 
        FROM 
            INSERTED;

    --do the job
    OPEN Cursor1;

    BEGIN TRY

        FETCH 
            NEXT 
        FROM 
            Cursor1 
        INTO 
            @Col1, 
            @Col2;

        WHILE @@FETCH_STATUS = 0
            BEGIN
                IF -- my condition
                    EXEC myProc1 @param1 = @Col1, @Param2 = @Col2;
                ELSE IF -- additional condition
                    EXEC myProc2 @param1 = @Col1, @Param2 = @Col2;

                FETCH 
                    NEXT 
                FROM 
                    Cursor1 
                INTO 
                    @Col1, 
                    @Col2;
            END;
    END TRY

    BEGIN CATCH
        -- Error Handling
    END CATCH

    --clean it up       
    CLOSE Cursor1;
    DEALLOCATE Cursor1;
END;

L'uso del pattern in questo modo riduce la duplicazione del codice o è necessario controllare lo stato del cursore. Fondamentalmente, l'inizializzazione del cursore dovrebbe essere sicura, come lo è la dichiarazione aperta. Una volta che il cursore è aperto, ti consigliamo di chiuderlo sempre a deallocare dalla sessione, e questa dovrebbe essere sempre un'azione sicura supponendo che il cursore sia stato aperto (che abbiamo appena stabilito dovrebbe essere sempre un'operazione sicura). In quanto tale, lasciare quelli al di fuori dei confini del Try-Catch significa che tutto può essere accuratamente chiuso alla fine, dopo il blocco Catch .

Vale la pena ricordare che ho specificato l'attributo READ_ONLY del cursore, nonché FORWARD_ONLY , poiché il tuo codice di esempio non scorreva avanti e indietro tra i record nel set. Se stai modificando le righe sottostanti in quelle procedure, probabilmente stai meglio usando un cursore STATIC per assicurarti di non causare accidentalmente un ciclo infinito. Questo non dovrebbe essere un problema poiché stai usando la tabella INSERTED per gestire il contesto del tuo cursore, ma vale comunque la pena menzionarlo per altri potenziali casi d'uso.

Se vuoi saperne di più sui cursori in SQL Server, consiglio vivamente di leggere questo post sul blog sull'argomento, mentre entra in dettaglio spiegando quali sono i vari modificatori di un cursore e gli effetti che hanno all'interno del Motore di database.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top