Home All Groups Group Topic Archive Search About
Author
7 Jan 2006 10:43 AM
sblar
In a stored procedure (SP1) I am looping through a cursor with records
from Table1. Each record in the cursor is inserted into Table2.
Insert trigger on Table2 is inserting the record into Table3 (in
another DB).
In the insert trigger on Table3, a series on checks are done on the
inserted record and in case of an error, an email is sent and the
trigger returns.
This break the cursorloop in SP1 and the rest of the records in the
cursor is not treated.
How do I make sure that all records are treated?

This is the flow:

-- SP1 ---------------------------------
DECLARE csrListe CURSOR FOR SELECT felt1 FROM Table1

OPEN csrListe

-- The first record is treated here....
  :

-- Treat the rest
WHILE @@FETCH_STATUS = 0 BEGIN
    FETCH NEXT FROM csrListe INTO @feltet
        IF @@FETCH_STATUS = 0 BEGIN
            blah-blah-blah
            INSERT INTO Table2 (Ordrenr, Status, Dato, Resultat) VALUES
(@Ordrenr, @Status, @Dato, @Result)
        END
END
CLOSE csrListe
DEALLOCATE csrListe

-- Table2_ITrig ---------------------------------
INSERT INTO db2.dbo.Table3 SELECT * FROM inserted

-- Table3_ITrig ---------------------------------
SET NOCOUNT ON

DECLARE @STATUS int
DECLARE @DATOTID smalldatetime
DECLARE @RESULT int


SELECT @ORDRENR =    (SELECT ORDRENR FROM INSERTED)
SELECT @STATUS =    (SELECT STATUS FROM INSERTED)
SELECT @DATOTID =    (SELECT DATO FROM INSERTED)
SELECT @RESULT =     (SELECT RESULT FROM INSERTED)

SET XACT_ABORT ON


IF NOT @STATUS IN (1,2,3,4,5,6,9,10) BEGIN
  SELECT @ERR = 'ERROR - unknown status = ' + CAST(@ORDRENR as char(4))
  UPDATE Table3 SET RESULTAT=2 WHERE ORDRENUMMER=@ORDRENR

  EXEC @rc = master.dbo.xp_smtp_sendmail
        @FROM    = N***@here.dk',
        @TO        = N'***@here.dk',
        @priority  = N'HIGH',
        @subject    = N'Status error',
        @message    = N'Status error',
        @type      = N'text/plain',
        @server    = 'smtp.here.dk'

  RETURN
END

The mail is send so it must be the final RETURN that is causing the
trouble.

Author
7 Jan 2006 10:52 AM
David Portas
sblar wrote:
Show quote
> In a stored procedure (SP1) I am looping through a cursor with records
> from Table1. Each record in the cursor is inserted into Table2.
> Insert trigger on Table2 is inserting the record into Table3 (in
> another DB).
> In the insert trigger on Table3, a series on checks are done on the
> inserted record and in case of an error, an email is sent and the
> trigger returns.
> This break the cursorloop in SP1 and the rest of the records in the
> cursor is not treated.
> How do I make sure that all records are treated?
>
> This is the flow:
>
> -- SP1 ---------------------------------
> DECLARE csrListe CURSOR FOR SELECT felt1 FROM Table1
>
> OPEN csrListe
>
> -- The first record is treated here....
>   :
>
> -- Treat the rest
> WHILE @@FETCH_STATUS = 0 BEGIN
>     FETCH NEXT FROM csrListe INTO @feltet
>         IF @@FETCH_STATUS = 0 BEGIN
>             blah-blah-blah
>             INSERT INTO Table2 (Ordrenr, Status, Dato, Resultat) VALUES
> (@Ordrenr, @Status, @Dato, @Result)
>         END
> END
> CLOSE csrListe
> DEALLOCATE csrListe
>
> -- Table2_ITrig ---------------------------------
> INSERT INTO db2.dbo.Table3 SELECT * FROM inserted
>
> -- Table3_ITrig ---------------------------------
> SET NOCOUNT ON
>
> DECLARE @STATUS int
> DECLARE @DATOTID smalldatetime
> DECLARE @RESULT int
>
>
> SELECT @ORDRENR =    (SELECT ORDRENR FROM INSERTED)
> SELECT @STATUS =    (SELECT STATUS FROM INSERTED)
> SELECT @DATOTID =    (SELECT DATO FROM INSERTED)
> SELECT @RESULT =     (SELECT RESULT FROM INSERTED)
>
> SET XACT_ABORT ON
>
>
> IF NOT @STATUS IN (1,2,3,4,5,6,9,10) BEGIN
>   SELECT @ERR = 'ERROR - unknown status = ' + CAST(@ORDRENR as char(4))
>   UPDATE Table3 SET RESULTAT=2 WHERE ORDRENUMMER=@ORDRENR
>
>   EXEC @rc = master.dbo.xp_smtp_sendmail
>         @FROM    = N***@here.dk',
>         @TO        = N'***@here.dk',
>         @priority  = N'HIGH',
>         @subject    = N'Status error',
>         @message    = N'Status error',
>         @type      = N'text/plain',
>         @server    = 'smtp.here.dk'
>
>   RETURN
> END
>
> The mail is send so it must be the final RETURN that is causing the
> trouble.

Do not use cursors in triggers. Doubly important, do not send email
from a trigger.

See:
http://groups.google.co.uk/group/microsoft.public.sqlserver.programming/browse_frm/thread/ab82308c47ed78/2b273c91159441f2

--
David Portas
SQL Server MVP
--
Author
7 Jan 2006 11:07 AM
sblar
Thanks David.
The cursor is in the sp not the trigger. I understand your point about
email though, and I will consider another path. But, this doesn't seem
to be the problem here as the email is sent.
Author
7 Jan 2006 11:31 AM
S=F8ren_Larsen
Thanks David, I understand your point about email and will consider
another path, but the problem here doesn't seem to be the email which
is sent ok. The cursor is in the sp not the trigger.

/Søren
Author
7 Jan 2006 12:01 PM
David Portas
"Søren Larsen" <sbl***@surfpost.dk> wrote in message
news:1136633487.767264.30440@g49g2000cwa.googlegroups.com...
Thanks David, I understand your point about email and will consider
another path, but the problem here doesn't seem to be the email which
is sent ok. The cursor is in the sp not the trigger.

/Søren


First, I suggest you get rid of the cursor. Use an INSERT ... SELECT
statement instead:

INSERT INTO Table2 (Ordrenr, Status, Dato, Resultat)
SELECT Ordrenr, Status, Dato, Resultat
  FROM ... ?

I expect there was more processing that you left out of your post but I can
only suggest a solution for what you posted.

Secondly, you need to modify your trigger to handle multiple rows properly.
Example:

/* This will FAIL if more than one row is inserted/updated */
SELECT @ORDRENR =    (SELECT ORDRENR FROM INSERTED)

If you don't send emails from the trigger then you won't need to assign the
column values to variables.

--
David Portas
SQL Server MVP
--
Author
7 Jan 2006 6:22 PM
Erland Sommarskog
David Portas (REMOVE_BEFORE_REPLYING_dpor***@acm.org) writes:
Show quote
> First, I suggest you get rid of the cursor. Use an INSERT ... SELECT
> statement instead:
>
> INSERT INTO Table2 (Ordrenr, Status, Dato, Resultat)
>  SELECT Ordrenr, Status, Dato, Resultat
>   FROM ... ?
>
> I expect there was more processing that you left out of your post but I
> can only suggest a solution for what you posted.
>
> Secondly, you need to modify your trigger to handle multiple rows
> properly.
> Example:
>
> /* This will FAIL if more than one row is inserted/updated */
> SELECT @ORDRENR =    (SELECT ORDRENR FROM INSERTED)
>
> If you don't send emails from the trigger then you won't need to assign
> the column values to variables.

Hey, I've already said all of that! (Except the point of not sending
mail from a trigger.) But I said it in a different newsgroup, as Søren
posted the message independently to two newsgroups. With the result
that I and David waste our time to say the same thing.

Please do not do that again!


--
Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se

Books Online for SQL Server 2005 at
http://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books.mspx
Books Online for SQL Server 2000 at
http://www.microsoft.com/sql/prodinfo/previousversions/books.mspx
Author
8 Jan 2006 1:38 PM
Søren Larsen
Show quote
"Erland Sommarskog" <esq***@sommarskog.se> skrev i en meddelelse
news:Xns9744C51E0DD29Yazorman@127.0.0.1...
> David Portas (REMOVE_BEFORE_REPLYING_dpor***@acm.org) writes:
>> First, I suggest you get rid of the cursor. Use an INSERT ... SELECT
>> statement instead:
>>
>> INSERT INTO Table2 (Ordrenr, Status, Dato, Resultat)
>>  SELECT Ordrenr, Status, Dato, Resultat
>>   FROM ... ?
>>
>> I expect there was more processing that you left out of your post but I
>> can only suggest a solution for what you posted.
>>
>> Secondly, you need to modify your trigger to handle multiple rows
>> properly.
>> Example:
>>
>> /* This will FAIL if more than one row is inserted/updated */
>> SELECT @ORDRENR =    (SELECT ORDRENR FROM INSERTED)
>>
>> If you don't send emails from the trigger then you won't need to assign
>> the column values to variables.
>
> Hey, I've already said all of that! (Except the point of not sending
> mail from a trigger.) But I said it in a different newsgroup, as Søren
> posted the message independently to two newsgroups. With the result
> that I and David waste our time to say the same thing.
>
> Please do not do that again!
>

Sorry to both of you. I never ment to waste anybodys time. I'm new to
news(!) and thought I would get more responses tis way. I see your point. It
will not happen again.

Anyway, thanks for your answers. I'm aware of the problem if my trigger
receives multiple rows, which it dont cause its only called from my SP with
the cursor loop. I would however very much like to avoid this way of
processing but I can't see how if I want to do some processing on each row
inserted. For example:

if Status = 1
  set @result = 2
if Status = 2
  set @result = 3

update sometable set Status = @Status where number = select number from
inserted

Any suggestions?

Thanks in advance
Søren
Author
8 Jan 2006 2:50 PM
Erland Sommarskog
Søren Larsen (sblar1_thisisno***@surfpost.dk) writes:
Show quote
> Anyway, thanks for your answers. I'm aware of the problem if my trigger
> receives multiple rows, which it dont cause its only called from my SP
> with the cursor loop. I would however very much like to avoid this way
> of processing but I can't see how if I want to do some processing on
> each row inserted. For example:
>
> if Status = 1
>   set @result = 2
> if Status = 2
>   set @result = 3
>
> update sometable set Status = @Status where number = select number from
> inserted
>
> Any suggestions?

Without knowledge of the business problem, it's difficult to suggest a
complete solution. But for the particular problem you appear to illustrate
you can use the CASE expression:

   UPDATE a
   SET    result = CASE b.status
                        WHEN 1 THEN 'OK'
                        WHEN 2 THEN 'OK with warnings'
                        WHEN 3 THEN 'Failed'
                        ELSE 'Complete disaster'
                   END
   FROM   a
   JOIN   b ON a.col = b.col


--
Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se

Books Online for SQL Server 2005 at
http://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books.mspx
Books Online for SQL Server 2000 at
http://www.microsoft.com/sql/prodinfo/previousversions/books.mspx
Author
8 Jan 2006 9:20 PM
Søren Larsen
Show quote
"Erland Sommarskog" <esq***@sommarskog.se> skrev i en meddelelse
news:Xns9745A1206A4E9Yazorman@127.0.0.1...
>
> Without knowledge of the business problem, it's difficult to suggest a
> complete solution. But for the particular problem you appear to illustrate
> you can use the CASE expression:
>
>   UPDATE a
>   SET    result = CASE b.status
>                        WHEN 1 THEN 'OK'
>                        WHEN 2 THEN 'OK with warnings'
>                        WHEN 3 THEN 'Failed'
>                        ELSE 'Complete disaster'
>                   END
>   FROM   a
>   JOIN   b ON a.col = b.col
>

Aha - thats neat. Does this mean that inserted are traversed and a.result
would be updated for every row in inserted and can it be done more than once
like this?

UPDATE a
  SET    result = CASE inserted.status
                       WHEN 1 THEN 'OK'
                       WHEN 2 THEN 'OK with warnings'
                       WHEN 3 THEN 'Failed'
                       ELSE 'Complete disaster'
                  END
  FROM   a
  JOIN   inserted ON a.col = inserted.col

UPDATE c
SET    somefield = CASE inserted.someotherfield
                       WHEN 1 THEN 11
                       WHEN 2 THEN 12
                       WHEN 3 THEN 13
                       ELSE 0
                  END
  FROM   c
  JOIN   inserted ON c.col = inserted.col

Will this do an update of both a and c based on inserted rows?

/Søren
Author
8 Jan 2006 10:48 PM
Erland Sommarskog
Søren Larsen (sblar1_thisisno***@surfpost.dk) writes:
Show quote
> Aha - thats neat. Does this mean that inserted are traversed and
> a.result would be updated for every row in inserted and can it be done
> more than once like this?
>
> UPDATE a
>   SET    result = CASE inserted.status
>                        WHEN 1 THEN 'OK'
>                        WHEN 2 THEN 'OK with warnings'
>                        WHEN 3 THEN 'Failed'
>                        ELSE 'Complete disaster'
>                   END
>   FROM   a
>   JOIN   inserted ON a.col = inserted.col
>
> UPDATE c
>  SET    somefield = CASE inserted.someotherfield
>                        WHEN 1 THEN 11
>                        WHEN 2 THEN 12
>                        WHEN 3 THEN 13
>                        ELSE 0
>                   END
>   FROM   c
>   JOIN   inserted ON c.col = inserted.col
>
> Will this do an update of both a and c based on inserted rows?

Yes. CASE is extremely powerful when working with set-based operations.
The above is a simplifed form. The more general form is like this:

   CASE WHEN somecolumn IN (1, 2, 3) THEN 'This'
        WHEN othercolumn = 'A' AND somecolumn = 34 THEN 'That'
        WHEN somecolumn < 0 THEN CASE col WHEN 1 THEN 'J' ELSE 'N' END
   END

So you can test for more general conditions, and you can nest CASE.
The conditions are always evaluated top-down, and evaluation stops
as soon one matches. If no condition is true, and there is on ELSE,
the value is NULL.

Important to understand is that CASE is an *expression*, and as a
an expression it always return the same data type. If you try:

    CASE something WHEN 1 THEN 0 ELSE 'x' END

this will fail, when something is not 1, because this CASE expression
returns an integer value, as integer is higher than char in the
data-type precedence in SQL Server.

> I almost forgot; why is the cursorloop broken in the first place? There
> was no error in trigger, unless RETURN is considered an error!

Was there a ROLLBACK? A rollback in a trigge aborts execution.





--
Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se

Books Online for SQL Server 2005 at
http://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books.mspx
Books Online for SQL Server 2000 at
http://www.microsoft.com/sql/prodinfo/previousversions/books.mspx
Author
9 Jan 2006 2:09 PM
Søren Larsen
Show quote
"Erland Sommarskog" <esq***@sommarskog.se> skrev i en meddelelse
news:Xns9745F24504C83Yazorman@127.0.0.1...
> Yes. CASE is extremely powerful when working with set-based operations.
> The above is a simplifed form. The more general form is like this:
>
>   CASE WHEN somecolumn IN (1, 2, 3) THEN 'This'
>        WHEN othercolumn = 'A' AND somecolumn = 34 THEN 'That'
>        WHEN somecolumn < 0 THEN CASE col WHEN 1 THEN 'J' ELSE 'N' END
>   END
>
> So you can test for more general conditions, and you can nest CASE.
> The conditions are always evaluated top-down, and evaluation stops
> as soon one matches. If no condition is true, and there is on ELSE,
> the value is NULL.
>
> Important to understand is that CASE is an *expression*, and as a
> an expression it always return the same data type. If you try:
>
>    CASE something WHEN 1 THEN 0 ELSE 'x' END
>
> this will fail, when something is not 1, because this CASE expression
> returns an integer value, as integer is higher than char in the
> data-type precedence in SQL Server.
>
>> I almost forgot; why is the cursorloop broken in the first place? There
>> was no error in trigger, unless RETURN is considered an error!
>
> Was there a ROLLBACK? A rollback in a trigge aborts execution.
>

Thats great, thank you Erland, I will try this to avois all my bad
structured triggers.

BTW there was no rollback, just the RETURN which I thought would go right
back to the "caller" and take the next record. This is the way I expect
things to work. Seen from a general programming view, a loop can only be
broken from within the loop itself, unless there is an error that brings the
application to a stop.

Sorry, but I'm not sure what happened exactly with all the identical
postings!
Author
9 Jan 2006 11:08 PM
Erland Sommarskog
Søren Larsen (sblar1_thisisno***@surfpost.dk) writes:
> BTW there was no rollback,

Unless xp_smtp_sendmail issues one.

> just the RETURN which I thought would go right back to the "caller" and
> take the next record. This is the way I expect things to work. Seen from
> a general programming view, a loop can only be broken from within the
> loop itself, unless there is an error that brings the application to a
> stop.

Well, it is not the loop as such that it is broken, it's the entire
batch.

Anyway, without the complete code, and a reproducible scenario, I cannot
give any better answer to the question that I have given now.

--
Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se

Books Online for SQL Server 2005 at
http://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books.mspx
Books Online for SQL Server 2000 at
http://www.microsoft.com/sql/prodinfo/previousversions/books.mspx
Author
10 Jan 2006 8:49 AM
Søren Larsen
"Erland Sommarskog" <esq***@sommarskog.se> skrev i en meddelelse
news:Xns97471828EF1FYazorman@127.0.0.1...
> Anyway, without the complete code, and a reproducible scenario, I cannot
> give any better answer to the question that I have given now.
>
I know. I appreciate your effort and will carry on from here.

/Søren
Author
8 Jan 2006 9:46 PM
Søren Larsen
I almost forgot; why is the cursorloop broken in the first place? There was
no error in trigger, unless RETURN is considered an error!

/Søren
Author
12 Jan 2006 7:16 PM
Søren Larsen
"Søren Larsen" <sblar1_thisisno***@surfpost.dk> skrev i en meddelelse
news:ODWS5zJFGHA.3036@tk2msftngp13.phx.gbl...
>I almost forgot; why is the cursorloop broken in the first place? There was
>no error in trigger, unless RETURN is considered an error!
>
> /Søren

I found the problem. There was no rollback but another cursor was invoked
during the process and I forgot to save @@FETCH_STATUS after each FETCH!!!
Need I say more?

/Søren
Author
12 Jan 2006 11:25 PM
Erland Sommarskog
Søren Larsen (sblar1_thisisno***@surfpost.dk) writes:
> I found the problem. There was no rollback but another cursor was invoked
> during the process and I forgot to save @@FETCH_STATUS after each FETCH!!!
> Need I say more?

See, had you posted the full code from the beginning, we would have spotted
this last week! :-)


--
Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se

Books Online for SQL Server 2005 at
http://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books.mspx
Books Online for SQL Server 2000 at
http://www.microsoft.com/sql/prodinfo/previousversions/books.mspx
Author
13 Jan 2006 9:40 PM
Søren Larsen
"Erland Sommarskog" <esq***@sommarskog.se> skrev i en meddelelse
news:Xns974A4569A57BYazorman@127.0.0.1...
> See, had you posted the full code from the beginning, we would have
> spotted
> this last week! :-)
>
I know - but the purpose of the code is kind of confidential so it would
have meant a lot of work for me to obscure it. I couldn't just leave parts
out, as I had no idea where the error was. Also I wouldn't have benefitted
from your excellent advice on CASE :o)

/Søren
Author
8 Jan 2006 9:46 PM
S=F8ren_Larsen
Hi David.

I know it's a bad thing to use cursors and I would really like to avoid
it. In this case I can't see how as I have to do a lot more processing
(as you expect) to each record and therefor have to make sure the
trigger receives one row at a time. How could that be done if the
trigger received multiple rows? For example:

if Status = 1 set somefield = 2
if Status = 2 set somefield = 3

and so on....

Thanks in advance.

Søren
Author
9 Jan 2006 2:02 AM
S=F8ren_Larsen
Hi David.

I know it's a bad thing to use cursors and I would really like to avoid
it. In this case I can't see how as I have to do a lot more processing
(as you expect) to each record and therefor have to make sure the
trigger receives one row at a time. How could that be done if the
trigger received multiple rows? For example:

if Status = 1 set somefield = 2
if Status = 2 set somefield = 3

and so on....

Thanks in advance.

Søren
Author
9 Jan 2006 2:18 AM
S=F8ren_Larsen
Hi David.

I know it's a bad thing to use cursors and I would really like to avoid
it. In this case I can't see how as I have to do a lot more processing
(as you expect) to each record and therefor have to make sure the
trigger receives one row at a time. How could that be done if the
trigger received multiple rows? For example:

if Status = 1 set somefield = 2
if Status = 2 set somefield = 3

and so on....

Thanks in advance.

Søren
Author
9 Jan 2006 2:39 AM
S=F8ren_Larsen
Hi David.

I know it's a bad thing to use cursors and I would really like to avoid
it. In this case I can't see how as I have to do a lot more processing
(as you expect) to each record and therefor have to make sure the
trigger receives one row at a time. How could that be done if the
trigger received multiple rows? For example:

if Status = 1 set somefield = 2
if Status = 2 set somefield = 3

and so on....

Thanks in advance.

Søren

AddThis Social Bookmark Button