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 INSERTASDECLARE @claimNum int, @location intSELECT @claimNum = claimID FROM insertedSELECT @location = location FROM insertedDECLARE @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 + ', ', '') + emailFROM MailGroupWHERE locationID = @locationSET @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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
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 intSELECT @claimNum = claimID FROM insertedSELECT @location = location FROM insertedDECLARE @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 + ', ', '') + emailFROM MailGroupWHERE locationID = @locationSET @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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
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 KizerMicrosoft MVP for Windows Server System - SQL Serverhttp://weblogs.sqlteam.com/tarad/Subscribe to my blog |
![Go to Top of Page Go to Top of Page](/forums/images/icon_go_up.gif) |
|
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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
tkizer
Almighty SQL Goddess
38200 Posts |
|
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 have23, 638, 139, 130, 4These 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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
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 have23, 638, 139, 130, 4These 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 KizerMicrosoft MVP for Windows Server System - SQL Serverhttp://weblogs.sqlteam.com/tarad/Subscribe to my blog |
![Go to Top of Page Go to Top of Page](/forums/images/icon_go_up.gif) |
|
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 queryClaims.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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
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 :DThank you all very much for your help on this. |
![Go to Top of Page Go to Top of Page](/forums/images/icon_go_up.gif) |
|
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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
tkizer
Almighty SQL Goddess
38200 Posts |
|
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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
tkizer
Almighty SQL Goddess
38200 Posts |
|
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 insertSELECT @claimNum = claimID FROM insertedIF @@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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
KrafDinner
Starting Member
34 Posts |
Posted - 2010-07-08 : 11:03:52
|
Thanks for all the advice everyone :DI 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 Go to Top of Page](/forums/images/icon_go_up.gif) |
|
Next Page
|