views:

1292

answers:

4

Hello Everyone,

This is my first question on StackOverflow, so please be nice ;)

Let's say I've got a trigger like this:

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

I want to be sure that Cursor1 is always closed and deallocated. Even myProc1 or myProc2 fails.

Shall I use try/catch block?

Thanks, Kamil

A: 

What you should do is never ever use a cursor in a trigger. Write correct set-based code instead. If someone did an import of data into your table of 100,000 new reiords you would lock up the table for hours and bring your database to a screaming halt. It is a very poor practice to use a cursor in a trigger.

HLGEM
If someone did an import of data into this table of 100,000 new records it wouldn't lock up my table at all (because this is a trigger after update ;) But seriously. Don't get me wrong. I totally understand what you mean and you are right. I would never ever use a cursor in a trigger unless this is absolutely necessary and in my case it is. In theory only few rows can be updated at the same time from my App (unless someone will update from Management Studio). Everything works perfectly (and fast) but I'm wondering what if one of my SPs fails. Should I worry at all?
Novitzky
I would suggest you test by making a change to the proc or the data insert that will guarantee the proc will fail. Then you will know what will happen.
HLGEM
Thanks. This is exactly what I am going to do. As I said before I have to use cursor (otherwise it would be too many changes in my app) but I can change my SPs. Thanks anyway.
Novitzky
+2  A: 

Required reading.

Jeremy Stein
Thanks. Very nice and useful article.
Novitzky
+1  A: 

Yes, use TRY/CATCH but make sure you deallocate etc after. Unfortunately, there is no finally in SQL Server.

However, I suggest wrapping this in another try/catch

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

Whether a cursor in a trigger is a good idea is a different matter...

gbn
+3  A: 

You could use the CURSOR_STATUS() function.

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

reference: http://msdn.microsoft.com/en-us/library/ms177609.aspx

turtles