Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2008 Forums
 Transact-SQL (2008)
 Send e-mail with query using a trigger?

Author  Topic 

KrafDinner
Starting Member

34 Posts

Posted - 2010-07-07 : 13:34:05
Hello,

I'm trying to send what I think should be a fairly simple e-mail using a trigger and I'm having major issues.

When I try to send the e-mail without the query attached, everything works fine. If I try to attach the query, it hangs. It doesn't return an error but instead just sits there "Executing query". The query should take less than 1 second and I've been patient and let it run for 5 minutes with still no results other than I have to restart my database in order to access any of the tables contained therein again.

If someone could help me out with what is going wrong, that would be greatly appreciated.

Here is the code used to create the trigger...

CREATE TRIGGER [dbo].[newClaim]
ON [dbo].[Claims]
AFTER INSERT
AS

DECLARE @claimNum int, @location int

SELECT @claimNum = claimID FROM inserted
SELECT @location = location FROM inserted

DECLARE @message varchar(500), @queryToRun varchar(300)

SET @message = 'A new claim has been filed! Please follow up on claim ' + CONVERT(VARCHAR(10), @claimNum) + ' and set up an appointment. ' +
'The damage will need to be inspected and photographs of the
damage need to be taken and logged. ' +
'The details of the claim are attached. '

DECLARE @recList varchar(max)
SELECT @recList = COALESCE(@recList + ', ', '') + email
FROM MailGroup
WHERE locationID = @location


SET @queryToRun = 'SELECT B.claimID, dateOpened, firstName, lastName FROM Claims.dbo.Customers A
INNER JOIN Claims.dbo.Claims B ON A.customerID = B.customerID WHERE B.claimID = ' + convert(varchar(10), @claimNum)


-- Use this area to SEND the email.
EXEC msdb.dbo.sp_send_dbmail
@profile_name='TestMail',
@recipients=@recList,
@subject='Test Claim E-mail',
@query=@queryToRun,
@attach_query_result_as_file='1',
@query_attachment_filename='Claim Details.txt',
@body=@message


If I use this trigger without the query, it works perfectly. I would just like to be able to attach data about the claim to the e-mail.

Any thoughts on why this hangs and will not work would be greatly appreciated. Thanks in advance!

Kristen
Test

22859 Posts

Posted - 2010-07-07 : 13:41:50
Personally I wouldn't put an Email in a Trigger. EMails "stall" for a variety of external reasons and will hold locks on the records for the duration of the process and so on.

Our approach is to insert rows into an "Email Queue Table" (from the Trigger if you like) and then have a task that runs and grabs rows from the EMail Queue Table and sends them out of the door. Easier to debug, and a single EMail routine (and queue) to have to manage, regardless of how many different routines there are that need to send Email.

Note also that your trigger assumes that the INSERT wll only be a single record. This may not be the case, of course, and you should always assume, in a trigger, that the [inserted] table contains multiple rows.
Go to Top of Page

KrafDinner
Starting Member

34 Posts

Posted - 2010-07-07 : 13:52:36
In this particular application, the insert will always be a single row.

If there's ever a case where I need to make changes to multiple rows at a time, I will just drop the trigger and recreate it afterward - in that case, I wouldn't want to spam the mail group.

Also, time is very important on these, so it is of the utmost urgency that the e-mail gets sent out immediately. That is why I did it with a trigger instead of some type of queue. I realize I could have a job with a very quick recurrence, but I wasn't sure there would be any benefit there.

If you could share some information on the benefits, that would be nice too. I'm relatively new to a lot of this and I was pretty much thrown in to sink or swim.

Thanks :D
Go to Top of Page

yosiasz
Master Smack Fu Yak Hacker

1635 Posts

Posted - 2010-07-07 : 13:59:58
What Kristen says is very true. I would just wrap

DECLARE @claimNum int, @location int

SELECT @claimNum = claimID FROM inserted
SELECT @location = location FROM inserted

DECLARE @message varchar(500), @queryToRun varchar(300)

SET @message = 'A new claim has been filed! Please follow up on claim ' + CONVERT(VARCHAR(10), @claimNum) + ' and set up an appointment. ' +
'The damage will need to be inspected and photographs of the
damage need to be taken and logged. ' +
'The details of the claim are attached. '

DECLARE @recList varchar(max)
SELECT @recList = COALESCE(@recList + ', ', '') + email
FROM MailGroup
WHERE locationID = @location


SET @queryToRun = 'SELECT B.claimID, dateOpened, firstName, lastName FROM Claims.dbo.Customers A
INNER JOIN Claims.dbo.Claims B ON A.customerID = B.customerID
WHERE B.claimID = ' + convert(varchar(10), @claimNum)


-- Use this area to SEND the email.
EXEC msdb.dbo.sp_send_dbmail
@profile_name='TestMail',
@recipients=@recList,
@subject='Test Claim E-mail',
@query=@queryToRun,
@attach_query_result_as_file='1',
@query_attachment_filename='Claim Details.txt',
@body=@message


into a sproc and call the sproc from trigger instead maybe?

<><><><><><><><><><><><><><><><><><><><><><><><><>
If you don't have the passion to help people, you have no passion
Go to Top of Page

KrafDinner
Starting Member

34 Posts

Posted - 2010-07-07 : 14:09:45
I tried that with the same result. The SP worked just fine when called by itself. The trigger worked fine up to the point of calling the SP. Debugging, it called the SP and stepped into it with no problem as well but somewhere in the actual sp_send_dbmail process it hangs up.

I'm not sure what is going on there or why I'm having the problem. If I took the query out of the sp I made and it sent just fine - same as it does in the trigger.

It must have something to do with my query in the actual send mail proc but I have no idea what could possibly be wrong with it.
Go to Top of Page

yosiasz
Master Smack Fu Yak Hacker

1635 Posts

Posted - 2010-07-07 : 14:28:21
have you tried sql profiler? see it when running from trigger and see it when running from seperate sproc.

<><><><><><><><><><><><><><><><><><><><><><><><><>
If you don't have the passion to help people, you have no passion
Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2010-07-07 : 14:31:14
You should not wrap it into a stored procedure and call it inside a trigger! Putting it into a stored procedure and calling the stored procedure inside a trigger means that the trigger is still doing the email component. You need to do the email asynchronously, which means that the email component is not done inside the trigger at all.

Instead you should write a row to a table that includes all of the email details. And then you would have a SQL Agent job that runs every minute query that table for any rows that need to be emailed.



Tara Kizer
Microsoft MVP for Windows Server System - SQL Server
http://weblogs.sqlteam.com/tarad/

Subscribe to my blog
Go to Top of Page

KrafDinner
Starting Member

34 Posts

Posted - 2010-07-07 : 14:50:08
It looks like I may be hitting some kind of lock...

Thanks for the suggestion of SQL Profiler - like I said, I'm relatively new to a lot of this stuff and hadn't even thought of that.

It had a wait time of a few minutes with the wait type of lock before I finally killed the process - it looks like it's not wanting to let me access the table when the trigger is fired and that must be why it is hanging on me ?

Any thoughts on this ?
Go to Top of Page

KrafDinner
Starting Member

34 Posts

Posted - 2010-07-07 : 14:52:20
It looks like to get done what I want done I'm going to have to do it with the suggested method of pushing the row to a table and mailing it later. Can anyone explain why this is, though ?

I'm curious as to why this is the case or why this is considered the best case. (Purely asking for my own understanding)

Thanks. :)
Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2010-07-07 : 14:52:44
Two of the posts were too wide, so I edited them down to get rid of the horizontal bars. Just an FYI.

Tara Kizer
Microsoft MVP for Windows Server System - SQL Server
http://weblogs.sqlteam.com/tarad/

Subscribe to my blog
Go to Top of Page

KrafDinner
Starting Member

34 Posts

Posted - 2010-07-07 : 15:24:59
Also, one other question...

What would be the best practice for making sure each one gets e-mailed ? They will have to be sent as separate e-mails because each one could potentially have a different list of recipients.

I realize a cursor would be an option to step through each row and send out the e-mail. Is there something else that would be a better practice?

Example: The mail queue table may simply contain a claimID and status. (There are different types of e-mails depending on the status)
If I have
23, 6
38, 1
39, 1
30, 4

These all need to be e-mailed to separate mail groups because they happened in separate locations and claims need to be taken up with the correct people. I have a table containing addresses based on location to query to build the recipient list. None of that is an issue, I'm just wondering if there is a better way to address this than running a cursor and stepping through to each and sending the mail.

Thanks for the information so far :)
Go to Top of Page

yosiasz
Master Smack Fu Yak Hacker

1635 Posts

Posted - 2010-07-07 : 15:28:08
Kristen's suggestion is on the money. my idea of wrapping it in a sproc is not good because you just moved the problem from the trigger to a sproc. to send an email to each person SSIS package is your best friend. You could do in a sproc too but SSIS is what this is for.

<><><><><><><><><><><><><><><><><><><><><><><><><>
If you don't have the passion to help people, you have no passion
Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2010-07-07 : 15:35:12
quote:
Originally posted by KrafDinner

Also, one other question...

What would be the best practice for making sure each one gets e-mailed ? They will have to be sent as separate e-mails because each one could potentially have a different list of recipients.

I realize a cursor would be an option to step through each row and send out the e-mail. Is there something else that would be a better practice?

Example: The mail queue table may simply contain a claimID and status. (There are different types of e-mails depending on the status)
If I have
23, 6
38, 1
39, 1
30, 4

These all need to be e-mailed to separate mail groups because they happened in separate locations and claims need to be taken up with the correct people. I have a table containing addresses based on location to query to build the recipient list. None of that is an issue, I'm just wondering if there is a better way to address this than running a cursor and stepping through to each and sending the mail.

Thanks for the information so far :)



In order to send multiple emails, you have to loop in some manner whether it be a cursor or a WHILE loop. There is no set-based way to do it. But there isn't really a performance hit with this as a cursor/WHILE loop is fine here.

Tara Kizer
Microsoft MVP for Windows Server System - SQL Server
http://weblogs.sqlteam.com/tarad/

Subscribe to my blog
Go to Top of Page

yosiasz
Master Smack Fu Yak Hacker

1635 Posts

Posted - 2010-07-07 : 15:40:31
also question for you. what happens when you add PRINT @allyourvars to the sproc and exec yoursproc. can you post these please. The other thing is the tables you are using in your query

Claims.dbo.Customers and Claims.dbo.Claims do you maybe have views for these tables you could use and/or use with (nolock) for them?

the last question is you are delimiting the email addresses with a comma. Did you want to delimit them using ; ?

Just trying to figure out what might be the problem



<><><><><><><><><><><><><><><><><><><><><><><><><>
If you don't have the passion to help people, you have no passion
Go to Top of Page

KrafDinner
Starting Member

34 Posts

Posted - 2010-07-07 : 15:56:37
The variables print correctly and the query even runs fine if I make it run in the sproc call.

I hadn't thought of views, but I have pretty much abandoned the initial process and am going to go with queuing in a table and then running a job to send the e-mail.

I realized I was delimiting with a comma - that was just what my fingers hit first when I was throwing it together as a quick test. I have since changed that to a ; but thanks for pointing it out :D

Thank you all very much for your help on this.
Go to Top of Page

KrafDinner
Starting Member

34 Posts

Posted - 2010-07-07 : 16:45:57
I actually have another question sort of along these same lines... except it's a question about efficiency instead of will it work.

Which would be preferable? To create a trigger for insert,update and then check in the trigger which it is and act accordingly OR to create two separate triggers, one for insert, one for update ?

Thanks ! :)
Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2010-07-07 : 17:05:44
I guess it depends how different the trigger code is going to be. If it's similar code, then use one trigger. If it's very different code, then use two triggers.

Tara Kizer
Microsoft MVP for Windows Server System - SQL Server
http://weblogs.sqlteam.com/tarad/

Subscribe to my blog
Go to Top of Page

KrafDinner
Starting Member

34 Posts

Posted - 2010-07-07 : 17:43:20
This seems like it's more from a logic standpoint than an efficiency standpoint ?

As in easier to debug and sort through two separate triggers if they do very different things than one that has an if and THEN does very different things.

Thanks again for your input :D
Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2010-07-07 : 17:53:17
Correct, at least that's my opinion. You can quickly and efficiently check if it's an update, insert, or delete by checking the inserted and deleted trigger tables (using IF EXISTS).

Tara Kizer
Microsoft MVP for Windows Server System - SQL Server
http://weblogs.sqlteam.com/tarad/

Subscribe to my blog
Go to Top of Page

Kristen
Test

22859 Posts

Posted - 2010-07-08 : 06:11:23
"In this particular application, the insert will always be a single row."

Then raise an error in your trigger if ever someone / some thing tries to insert two+ rows, otherwise you are just building in a bug where one, random, record from an INSERT triggers an email from a bulk or multi-row insert

SELECT @claimNum = claimID FROM inserted
IF @@ROWCOUNT <> 1 RAISERROR(...

"Also, time is very important on these, so it is of the utmost urgency that the e-mail gets sent out immediately"

SMTP is very variable. I think you will be at the back of the SMTP queue, but even if not there are lots of other issues like having no ability to resend later (e.g. to recover from "Mailbox full" bounces, SMTP service accidentally stopped, and so on), plus you have a log of what was sent - which comes down to being able to re-send stuff when something goes T.U . You can set your polling routine to run continuously - if it finds no rows to process it can pause for, say, 10 seconds (or 1 second if you must), its not going to be long. Our email table has a Priority Column so the "Lost password" emails go out at priority 1, whereas Order Confirmation and Marketing EMails go out at lower priority. If you just try to send direct you will be in the same SMTP Queue as the Marketing emails coming from another job

If you feel you have to do it this way don't do it in the trigger, have an SProc for the Insert and do it in there so you are not tying the Insert to the Trigger transaction, and also you will have an isolate piece of code which will be much easier to test & debug than something sitting on every insert route to the table.

If Email performance is an issue for you (and assuming you have more than a handful to be sent each day ) then look at a tool that can operate multiple SMTP channels concurrently - so the one slow email to some crappy MX won't stall your whole database. We've used SMTP Express (I've forgotten the name of the people, but they also sold "Easy Mail Objects" or something like that) in the past and it was very good, long time ago though so there may be better alternatives out there now.
Go to Top of Page

KrafDinner
Starting Member

34 Posts

Posted - 2010-07-08 : 11:03:52
Thanks for all the advice everyone :D

I hadn't thought about raising an error there in the case of multi-row, so thank you for that suggestion.

I have essentially worked around even needing a query in the e-mail at all, but thanks again for the suggestions everyone.
Go to Top of Page
    Next Page

- Advertisement -