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.
| Author |
Topic |
|
dprichard
Yak Posting Veteran
94 Posts |
Posted - 2006-10-02 : 15:54:20
|
I have a trigger I am working on. I am trying to say if the request_status = 3 then send out an approved email, if status = 4 send out the denied email. The approved email is working, but the denied email is not. Any help would be greatly appreciated.Here is my code:ALTER TRIGGER requestupdateON dbo.requestFOR UPDATEASBEGINDECLARE @mailtoaddy NVARCHAR(128), @approvedbody NVARCHAR(MAX), @deniedbody NVARCHAR(MAX), @status INT;SET @mailtoaddy = (SELECT employee.emp_emailFROM INSERTED INNER JOIN employee ON inserted.emp_id = employee.emp_id)SET @approvedbody = N'<font size="2"><strong>TIME OFF REQUEST CONFIRMATION</strong></font><br><br>'+(SELECT employee.emp_fnameFROM INSERTED INNER JOIN employee ON inserted.emp_id = employee.emp_id)+N',<br><br> Your time off request has been approved.<br><br>Thank you and have a nice day!<br><br><strong>Management</strong>'SET @deniedbody = N'<font size="2"><strong>TIME OFF REQUEST CONFIRMATION</strong></font><br><br>'+(SELECT employee.emp_fnameFROM INSERTED INNER JOIN employee ON inserted.emp_id = employee.emp_id)+N',<br><br> Your time off request has been denied.<br><br>Thank you and have a nice day!<br><br><strong>Management</strong>'SET @status = (select request_status FROM INSERTED);IF (@status = '3')BEGINEXEC msdb.dbo.sp_send_dbmail @recipients = @mailtoaddy, @subject = 'TIME OFF REQUEST UPDATE', @body = @approvedbody, @body_format = 'HTML';IF (@status = '4')BEGINEXEC msdb.dbo.sp_send_dbmail @recipients = @mailtoaddy, @subject = 'TIME OFF REQUEST UPDATE', @body = @deniedbody, @body_format = 'HTML';ENDENDEND |
|
|
Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)
7020 Posts |
Posted - 2006-10-02 : 17:55:06
|
| Sending email is not the kind of thing you should be using a trigger for. You should send the email from somwhere else in your application.Also, your trigger is not designed to handle more than one row in the result set.CODO ERGO SUM |
 |
|
|
dprichard
Yak Posting Veteran
94 Posts |
Posted - 2006-10-02 : 19:40:07
|
| I am trying to kick out an email on update or insert. There will never be a situation on this table where more than one record will be updated at a time. I am pretty new to SQL. Only been working with it a few weeks. Where should I be putting this email info? |
 |
|
|
Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)
7020 Posts |
Posted - 2006-10-02 : 19:59:23
|
| >>>I am trying to kick out an email on update or insert.That's another problem with the trigger, since it is an UPDATE trigger and doesn't fire on INSERT.>>>There will never be a situation on this table where more than one record will be updated at a time.Famous last words. You should NEVER write a trigger with this assumption; it's alway wrong. >>>Where should I be putting this email info?I would suggest adding this to the stored procedures that do the inserts or updates. Or add it to your front-end application.CODO ERGO SUM |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2006-10-03 : 01:07:17
|
Take note of what MVJ said:"I am pretty new to SQL"means you are going to have trouble with:"There will never be a situation on this table where more than one record will be updated at a time"At the very least put a check for COUNT(*) from INSERTED = 1 and use RAISERROR to bail out when it updates multiple rows.Triggers need to happen very quickly, and without any external interaction. You can water down either of those ideals, of course, but it would be wise to try not to!If you cannot see a way to adding the emailing logic "upstream" then you could add the Email to a table of "emails to send" and have a scheduled task run every few minutes that grabs a row from the "ToBeSent" Email table, sends the email, then deletes the row. (And if people start getting 1,000 emails an hour you can then change the emailing task to amalgamate emails to the same person.Indenting your code would probably help you with the problem with your Logic:IF (@status = '3')BEGIN EXEC msdb.dbo.sp_send_dbmail @recipients = @mailtoaddy, @subject = 'TIME OFF REQUEST UPDATE', @body = @approvedbody, @body_format = 'HTML'; IF (@status = '4') BEGIN EXEC msdb.dbo.sp_send_dbmail @recipients = @mailtoaddy, @subject = 'TIME OFF REQUEST UPDATE', @body = @deniedbody, @body_format = 'HTML'; ENDEND and you will see that @status = '4' only happens within the @status = '3' section - which clearly will never be executed!You can combine your 4 SET statements into one, which will be a lot more efficient. SQL is about doing things in "Sets", if you are used to programming in sequential-languages this is a big mind-shift.SELECT @mailtoaddy = employee.emp_email, @approvedbody = N'<font size="2"><strong>TIME OFF REQUEST CONFIRMATION</strong></font><br><br>' + employee.emp_fname + N',<br><br> Your time off request has been approved.<br><br>Thank you and have a nice day!<br><br><strong>Management</strong>', @deniedbody = N'<font size="2"><strong>TIME OFF REQUEST CONFIRMATION</strong></font><br><br>' + employee.emp_fname + N',<br><br> Your time off request has been denied.<br><br>Thank you and have a niceday!<br><br><strong>Management</strong>', @status = inserted.request_status and once you've got to that point you can then have:SELECT @mailtoaddy = employee.emp_email, @body = CASE WHEN inserted.request_status = '3' THEN N'<font size="2"><strong>TIME OFF REQUEST CONFIRMATION</strong></font><br><br>' + employee.emp_fname + N',<br><br> Your time off request has been approved.<br><br>Thank you and have a nice day!<br><br><strong>Management</strong>' WHEN inserted.request_status = '4' THEN N'<font size="2"><strong>TIME OFF REQUEST CONFIRMATION</strong></font><br><br>' + employee.emp_fname + N',<br><br> Your time off request has been denied.<br><br>Thank you and have a niceday!<br><br><strong>Management</strong>' ENDFROM ...WHERE inserted.request_status IN ('3', '4')EXEC msdb.dbo.sp_send_dbmail @recipients = ...and you could easily change that to a style of:INSERT INTO EMailWaitingToBeSent(recipients, subject, body, body_format)SELECT employee.emp_email, 'TIME OFF REQUEST UPDATE', CASE WHEN inserted.request_status = '3' THEN ... WHEN inserted.request_status = '4' THEN ... END, 'HTML'FROM INSERTED INNER JOIN ... and it will work for however many records happen to be updated in the trigger - in the future!What is msdb.dbo.sp_send_dbmail? Unless that's some newfangled MS System Procedure in SQL2005 it seems a bit scary that a user-SProc has been added to msdb ...Kristen |
 |
|
|
dprichard
Yak Posting Veteran
94 Posts |
Posted - 2006-10-03 : 11:53:31
|
Kristen,I wasn't trying to dispute any info from the other replys, I am just new and trying to understand the why's behind what I am doing versus the steps so I have a better understanding. I appreciate your post, but it went way over me so please be patient if I am bugging you with all my newbie questions.This is where I got the information on the sp_send_dbmail from:http://msdn2.microsoft.com/en-us/library/ms190307.aspxAs far as this part of your post:quote: If you cannot see a way to adding the emailing logic "upstream" then you could add the Email to a table of "emails to send" and have a scheduled task run every few minutes that grabs a row from the "ToBeSent" Email table, sends the email, then deletes the row. (And if people start getting 1,000 emails an hour you can then change the emailing task to amalgamate emails to the same person.
When you say add email upstream, I am not sure what you mean (Cause I am new). What I am trying to get my app to do is the user submits a request for time off and it goes into the request table. I need to email the user saying it was done successfully and email the supervisor telling them there is a new request. I setup a similar trigger for the insert that does this. Then when they supervisor updates the request, I need to kick an email back saying whether it was approved or denied. I am doing this with the triggers right now cause I posted a few times trying to figure out how best to do this with no response and this was the only way I could figure out how. So, it seems I steered my ship in the wrong direction and I want to correct it, but I just want to understand this a little better.Thank in advance for any help!!!!!!!!!! |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2006-10-03 : 12:02:02
|
| "When you say add email upstream"Sorry, I meant "before you get to the trigger" - so that would be Stored Procedure, or Application, most likely."but it went way over me"As you said you were new to SQL I tried to break it down into steps ... so fire away with any questions you may have.Kristen |
 |
|
|
dprichard
Yak Posting Veteran
94 Posts |
Posted - 2006-10-03 : 13:01:00
|
| So, I have a stored procedure for the insert statement right now. You are saying that I should put the Email part in the stored procedure with the insert? |
 |
|
|
dprichard
Yak Posting Veteran
94 Posts |
Posted - 2006-10-03 : 13:31:24
|
| Okay, I am reading about jobs and stored procedures and am wondering if this is what you are talking about. Setup a stored procedure to send out the email then setup a scheduled task to hit the stored procedure say once a day or once an hour to send them out? |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2006-10-03 : 14:03:48
|
| "You are saying that I should put the Email part in the stored procedure with the insert?"Your application will, most probably, talk to SQL Server either through a Stored Procedure or by using Dynamic SQL (SQL embedded in the application)If you use a Stored Procedure AND all such inserts go through that SProc, its quite a nice place to trigger the Email from.However, you will be triggering the Email ON THE SQL BOX and not on, for example, the Web Box / Client. For us this would be a no-no as we have very tight controls on our SQL boxes and sending bulk email is not something we allow.So for us we would have to put the Email details into a table and have something else interrogate that table periodically and send the emails out - in our case a little VB application that just requests data from the EmailToBeSent table, and send the email out.Now, if YOU CAN send Emails from your SQL box you can either do them in the SProc that inserts the row, or by a batch job.So as to your second point you could schedule a stored procedure to run once an hour and send the Emails.Or you could schedule a VB Task (or similar) on a different server to do the emails.One of the potential benefits of sending them in batches is that all types of emails can be sent by a single procedure - its just got to read the Recipient/Subject/Message from the table, send the email, and then flag the record as "done".So if you discovered you needed to change your Email process somehow - log that they were sent, change the SMTP server, BlindCarbon the office manager, whatever, then there is only one Sproc responsible for sending emails and changing it is centralised. You could even easily change it from using an SProc to send them on the SQL box to having a little VB Application on some other box.Kristen |
 |
|
|
dprichard
Yak Posting Veteran
94 Posts |
Posted - 2006-10-03 : 15:06:25
|
This is the stored procedure I am using for the insert statement:ALTER PROCEDURE dbo.insNewRequest @emp_id bigint, @request_start_date datetime, @request_end_date datetime, @request_duration int, @request_notes text, @time_off_id bigint /* ( @parameter1 int = 5, @parameter2 datatype OUTPUT ) */AS /* SET NOCOUNT ON */ INSERT request ( emp_id, request_submit_date, request_start_date, request_end_date, request_duration, request_notes, time_off_id )Select @emp_id, GETDATE(), @request_start_date, @request_end_date, 1 + (DATEDIFF(day, @request_start_date, @request_end_date)) - (select count(*) from WeekEndsAndHolidays where DayOfWeekDate between @request_start_date and @request_end_date), @request_notes, @time_off_id; I tried to mixing the two without much luck. It didn't like the FROM INSERTED. It is telling me Invalid object name 'INSERTED'. |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2006-10-03 : 15:20:08
|
Yup, the INSERTED pseudo table is only available in a trigger.I reckon that, after your INSERT statement, you need to add something like this:SELECT @mailtoaddy = employee.emp_email, @body = CASE WHEN request.request_status = '3' THEN N'<font size="2"><strong>TIME OFF REQUEST CONFIRMATION</strong></font><br><br>' + employee.emp_fname + N',<br><br> Your time off request has been approved.<br><br>Thank you and have a nice day!<br><br><strong>Management</strong>' WHEN request.request_status = '4' THEN N'<font size="2"><strong>TIME OFF REQUEST CONFIRMATION</strong></font><br><br>' + employee.emp_fname + N',<br><br> Your time off request has been denied.<br><br>Thank you and have a niceday!<br><br><strong>Management</strong>' ENDFROM request INNER JOIN employee ON request.emp_id = employee.emp_idWHERE request.request_status IN ('3', '4') AND request.emp_id = @emp_idEXEC msdb.dbo.sp_send_dbmail @recipients = ...Kristen |
 |
|
|
dprichard
Yak Posting Veteran
94 Posts |
Posted - 2006-10-03 : 16:04:17
|
I can't find info on WHEN and the way you use it here. If you were trying to find more info on that what would you google. I want to read up on that please!Okay, so this is what I am using now, but it looks like the @mailtoaddy = employee.emp_email isn't picking up the information. I get the following error:At least one of the following parameters must be specified. "@recipients, @copy_recipients, @blind_copy_recipients".The way I was doing it before was assigning the @mailaddy = (SELECT STATEMENT)ALTER PROCEDURE dbo.insNewRequest @emp_id bigint, @request_start_date datetime, @request_end_date datetime, @request_duration int, @request_notes text, @time_off_id bigint /* ( @parameter1 int = 5, @parameter2 datatype OUTPUT ) */AS /* SET NOCOUNT ON */ INSERT request ( emp_id, request_submit_date, request_start_date, request_end_date, request_duration, request_notes, time_off_id )Select @emp_id, GETDATE(), @request_start_date, @request_end_date, 1 + (DATEDIFF(day, @request_start_date, @request_end_date)) - (select count(*) from WeekEndsAndHolidays where DayOfWeekDate between @request_start_date and @request_end_date), @request_notes, @time_off_id; DECLARE @mailtoaddy NVARCHAR(128),@body NVARCHAR(MAX);SELECT @mailtoaddy = employee.emp_email, @body = CASE WHEN request.request_status = '3' THEN N'<font size="2"><strong>TIME OFF REQUEST CONFIRMATION</strong></font><br><br>' + employee.emp_fname + N',<br><br> Your time off request has been approved.<br><br>Thank you and have a nice day!<br><br><strong>Management</strong>' WHEN request.request_status = '4' THEN N'<font size="2"><strong>TIME OFF REQUEST CONFIRMATION</strong></font><br><br>' + employee.emp_fname + N',<br><br> Your time off request has been denied.<br><br>Thank you and have a niceday!<br><br><strong>Management</strong>' ENDFROM request INNER JOIN employee ON request.emp_id = employee.emp_idWHERE request.request_status IN ('3', '4') AND request.emp_id = @emp_idEXEC msdb.dbo.sp_send_dbmail @recipients = @mailtoaddy, @subject = 'EMPLOYEE VACATION REQUEST NOTIFICATION', @body = @body, @body_format = 'HTML'; |
 |
|
|
dprichard
Yak Posting Veteran
94 Posts |
Posted - 2006-10-03 : 16:05:59
|
| Please disreguard the part about WHEN. I typed "SELECT CASE WHEN THEN" in google and found info on that. |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2006-10-04 : 01:43:00
|
| Yup, its "CASE" you are after, rather than "WHEN". Glad you found it!Note that I took your email logic from your UPDATE trigger, and this is your INSERT SProc - in particular it is testing request.request_status to decide which type of email to send, and that column is NOT part of the Insert statement - so will only ever be set to the default.I guess that instead of that you need to be sending the "You time off request is being considered" to the employee and "Please consider this request" to the manager?Kristen |
 |
|
|
dprichard
Yak Posting Veteran
94 Posts |
Posted - 2006-10-04 : 16:28:30
|
Duh, I am sorry. I didn't even notice I was combining the two. This is my final result for the insert. Can you tell me if this looks okay and if I am going to run into any issues doing it this way? I ended up selecting the top one ordered by reqeuest desc because it was dying cause it was grabbing every row with the employee id and it was saying I couldn't do more than one result.ALTER PROCEDURE dbo.insNewRequest @emp_id bigint, @request_start_date datetime, @request_end_date datetime, @request_duration int, @request_notes text, @time_off_id bigintAS INSERT request ( emp_id, request_submit_date, request_start_date, request_end_date, request_duration, request_notes, time_off_id )Select @emp_id, GETDATE(), @request_start_date, @request_end_date, 1 + (DATEDIFF(day, @request_start_date, @request_end_date)) - (select count(*) from WeekEndsAndHolidays where DayOfWeekDate between @request_start_date and @request_end_date), @request_notes, @time_off_id; DECLARE @mailtoaddy NVARCHAR(128), @body NVARCHAR(MAX);SET @mailtoaddy = (SELECT TOP (1)employee.emp_emailFROM request INNER JOIN employee ON request.emp_id = employee.emp_idWHERE request.emp_id = @emp_idORDER BY request.request_id DESC)SET @body = N'<font size="2"><strong>TIME OFF REQUEST CONFIRMATION</strong></font><br><br>'+(SELECT TOP (1) employee.emp_fnameFROM request INNER JOIN employee ON request.emp_id = employee.emp_idWHERE request.emp_id = @emp_idORDER BY request.request_id DESC)+N' You have successfully submitted your time off request. You will receive another confirmation once the request has been reviewed<br><br>Thank you and have a nice day!<br><strong>Management</strong>';EXEC msdb.dbo.sp_send_dbmail @recipients = @mailtoaddy, @subject = 'EMPLOYEE TIME OFF REQUEST CONFIRMATION', @body = @body, @body_format = 'HTML'; |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2006-10-05 : 01:49:14
|
Couple of things:You have abandoned the style I had which only queries the EMPLOYEE once, you are doing the same query for each SET command.I JOINed the EMPLOYEE and REQUEST table to get the request status - as I said I was doing that from your UPDATE logic. You don't need that at all for how you now have it - there is NO test on any request columns, so you just want:FROM employee WHERE employee.emp_id = @emp_idIt is important that you understand why this was wrong, so please ask if its not clear - otherwise you will be blindly following rather than understanding what you are doing, and then I won't have been of any real help!!Have a look at this and see if it makes sense:SELECT @mailtoaddy = employee.emp_email, @body = N'<font size="2"><strong>TIME OFF REQUEST CONFIRMATION</strong></font><br><br>' + employee.emp_fnameFROM employeeWHERE employee.emp_id = @emp_id Kristen |
 |
|
|
dprichard
Yak Posting Veteran
94 Posts |
Posted - 2006-10-05 : 07:38:38
|
Oh, I absolutely see what you are saying. I was overcomplicating it.SELECT @mailtoaddy = employee.emp_email, @body = N'<font size="2"><strong>TIME OFF REQUEST CONFIRMATION</strong></font><br><br>' + employee.emp_fname + N',<br><br>This email is to confirm you have successfully submitted your time off request. You will be sent an update email after your request has been approved. <br><br>Thank you and have a nice day!<br><strong>Management</strong><br>'FROM employeeWHERE employee.emp_id = @emp_id Thank you so so so so so much for all your help with this! |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2006-10-05 : 13:28:03
|
| Hehehe ... you got it! Move on to the next problem!! |
 |
|
|
|
|
|
|
|