Does anyone know why this code is sending users duplicate emails? - sql

We have a stored procedure that is supposed to check db and select all records where sentFlag is No.
Once record(s) found, the stored proc invokes sp_send_dbmail with passed parameters and then sends an email to affected individuals.
This appears to work.
The issue we are having so far though is that each indivual is receiving duplicate emails.
Any ideas what part of this code could be causing this?
OPEN MAIL_CURSOR
FETCH MAIL_CURSOR into #mail1, #sender,#content1
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #email = #email+';'+Email
FROM GRVRIEVANCES
WHERE sentFlag = 'No'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'Grievances',
#recipients = #email,
#subject = 'Account Details',
#body = #content1;
FETCH MAIL_CURSOR INTO #mail1, #sender, #content1
END
CLOSE MAIL_CURSOR
DEALLOCATE MAIL_CURSOR
END

If you set email to an initial value within the loop, does the issue go away? Also, make sure you setting the sentflag to 'yes'.
WHILE ##FETCH_STATUS = 0
BEGIN
SET #email=''
SELECT #email = #email+';'+Email

Back to basics to solve this one.
Start with some debugging:
Comment out your EXEC part
Add PRINT #email in the same spot
Run the cursor and see the reults, they should be quite enlightening!
Essentially what you're doing is on every cursor execution you are building up this big ol' string of email addresses for all GRVRIEVANCES WHERE sentFlag = 'No'.

Related

Only few mails getting delivered using sp_send_dbmail in SQL Server 2008

I am extracting the recipient list from a column in a table and I am running the stored procedure sp_send_dbmail to send the emails to these recipients.
There are a total of around 200 recipients.
Ironically only few of the mails are delivered even though the message that I am getting is Mail queued. The database mail is correctly configured and I am using the Public database profile.
When I check the msdb.dbo.sysmail_mailitems table, as expected value in sent_status column is 1 for the recipients for whom the mail is delivered and for rest of them the value is either 0 or 2.
I am completely sure that the recipient list is 100% correct. Do we have any workaround to resolve this issue?
Below is the code that I am running:
CREATE procedure [dbo].[sp_dataRefreshNotification]
AS
BEGIN
DECLARE #ToMail VARCHAR(20)
DECLARE #Body1 VARCHAR(MAX) =
'Dear User,
Data has been refreshed.
Regards,
IT Support Team
Note: This is an auto generated e-mail, please do not reply this mail. '
SELECT DISTINCT RecipientAddress FROM dbo.RecipientAddressList
OPEN notification_cursor
FETCH NEXT FROM notification_cursor
INTO #ToMail
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC msdb.dbo.sp_send_dbmail
#profile_name ='aaaa',
#Recipients = #Tomail,
#Subject = 'Required Data',
#Body = #Body1
FETCH NEXT FROM notification_cursor
INTO #ToMail
END
CLOSE notification_cursor
DEALLOCATE notification_cursor
END
Use Wait for Delay function which will break each instance of your cursor.
CREATE procedure [dbo].[sp_dataRefreshNotification]
AS
BEGIN
DECLARE #ToMail VARCHAR(20)
DECLARE #Body1 VARCHAR(MAX) =
'Dear User,
Data has been refreshed.
Regards,
IT Support Team
Note: This is an auto generated e-mail, please do not reply this mail. '
SELECT DISTINCT RecipientAddress FROM dbo.RecipientAddressList
OPEN notification_cursor
FETCH NEXT FROM notification_cursor
INTO #ToMail
WHILE ##FETCH_STATUS = 0
BEGIN
Waitfor Delay '000:00:10'
EXEC msdb.dbo.sp_send_dbmail
#profile_name ='aaaa',
#Recipients = #Tomail,
#Subject = 'Required Data',
#Body = #Body1
FETCH NEXT FROM notification_cursor
INTO #ToMail
END
CLOSE notification_cursor
DEALLOCATE notification_cursor
END
ouch you do not need a cursor here, same thing can be done using a simple select query, see below
CREATE PROCEDURE [dbo].[sp_dataRefreshNotification]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #ToMail VARCHAR(MAX);
DECLARE #Body1 VARCHAR(MAX);
SET #Body1 = 'Dear User, ' + CHAR(10) +
'Data has been refreshed. ' + CHAR(10) +
'Regards, ' + CHAR(10) +
'IT Support Team ' + CHAR(10) + CHAR(10) +
'Note: This is an auto generated e-mail, please do not reply this mail. ' + CHAR(10)
SELECT #ToMail = COALESCE(#ToMail+';' ,'') + RecipientAddress
FROM (SELECT DISTINCT RecipientAddress
FROM dbo.RecipientAddressList) t
EXEC msdb.dbo.sp_send_dbmail #profile_name ='aaaa'
,#Recipients = #Tomail
,#Subject = 'Required Data'
,#Body = #Body1
END
If you really need to send a separate email to each individual, then your method is good using the loop and a WAITFOR DELAY '00:00:10' in the loop. But if you could send one email to all individuals, then you could concatenate these emails. Only there is a maximum of 255 characters of concatenated emails that can be in the recipient box. So you'd have to break the resulted string into pieces or 255 characters of less.

sp_send_dbmail sending mail even if condition is false - SQL

I have an IF statement part of a stored procedure as follows :
BEGIN TRY
IF EXISTS (SELECT which I have confirmed returns no results, has a variable called from another table in the where clause)
BEGIN
EXEC msdb.dbo.sp_send_dbmail
#profile_name = #dynamic_profile,
#recipients = #dynamic_recipient,
#subject = #subjectline,
#body = #mailHTML,
#body_format = 'HTML';
END
END TRY
This is tied to a job that runs every 15mins.
Why is it keep sending me blank emails when the IF statement asks to skip the mail sending if the select returns no results?
My NOCOUNT is set to ON
Try - instead:
IF (SELECT COUNT(*) FROM whatever) > 0
BEGIN
--MAIL
END

Bulk Update Email Trigger

I created a update trigger in Microsoft SQL Server that would email me if a date changed in a row.
Similar to this:
IF UPDATE(ColumnName)
BEGIN
DECLARE #columnVal AS DATETIME
SELECT
#columnVal = i.columnName
FROM
inserted i JOIN deleted d on i.RowId= d.RowId;
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'me',
#recipients = 'me#company.com',
#body = 'blah blah datechange',
#body_format = 'HTML',
#subject = 'subject';
END
it worked fine for a time.
Then I switched over to batch updates and only the first row email is sent out if the date changes on multiple rows. I tried to set up a cursor to roll through the changes but I cannot get it to work, Similar to below:
DECLARE #columnVal AS DATETIME
DECLARE cur CURSOR LOCAL READ_ONLY FAST_FORWARD FOR
SELECT
i.ColumnName
FROM
inserted i JOIN deleted d on i.RowId= d.RowId;
OPEN cur
FETCH NEXT FROM cur INTO #columnVal
WHILE ##FETCH_STATUS = 0
BEGIN
IF UPDATE(ColumnName)
BEGIN
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'me',
#recipients = 'me#company.com',
#body = 'blah blah datechange',
#body_format = 'HTML',
#subject = 'subject';
END
FETCH NEXT FROM cur INTO #columnVal
END
CLOSE cur
DEALLOCATE cur
Any ideas on how to accomplish this task? Would the Update(ColumnName) function even work properly nested inside a cursor (would it actually tell me if that column was updated for that row?)
A cursor inside a trigger is a very very bad idea.
A trigger should be very lean - it shouldn't do a lot of work! I would recommend to only "take a note" of who you have to send an e-mail to - but leave the actual sending of the e-mail to a separate e.g. SQL Server Agent Job which is not part of the trigger.
Triggers are fired often and often unexpectedly - don't put a log of processing burden into them! And especially not a performance killer like a cursor!
To find those rows that you're interested in, you can use a WHERE clause something like:
WHERE inserted.ColumnName <> deleted.ColumnName
In the context of an UPDATE trigger, this means the new value of ColumnName is different from the old value --> this column has been updated.

Agent job that emails from custom email table

I used to have triggers in my database that used cursors / sp_send_dbmail to email when certain columns were updated. I was told this was not best practice so I created a new table called EmailNotify that contains columns like recepient, subject, body etc. So instead the triggers now insert into this table the email I want to send.
I want to create a Job that runs every few minutes that checks this table and emails. The item below is what I came up with but is it okay to use cursors in this case? Should the table include a sent field so I know which rows I sent? Can I change that inside the cursor? Or would it be recommended to truncate the table afterwards?
DECLARE #emailSubject AS NVARCHAR(100);
DECLARE #emailRecipients AS NVARCHAR(100);
DECLARE #emailBody AS NVARCHAR(max);
DECLARE cur CURSOR LOCAL READ_ONLY FAST_FORWARD FOR
SELECT
recipients,
subject,
body
FROM
EmailNotify;
OPEN cur
FETCH NEXT FROM cur INTO
#emailRecipients,
#emailSubject,
#emailBody
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'name',
#recipients = #emailRecipients,
#body = #emailBody,
#body_format = 'HTML',
#subject = #emailSubject;
FETCH NEXT FROM cur INTO
#emailRecipients,
#emailSubject,
#emailBody
END
CLOSE cur
DEALLOCATE cur
I would
Add an EmailSent(DATETIME) = NULL column to your table
Create an EmailSent variable at the top of your proc and set it to GETDATE()
UPDATE <YOURTABLE> SET EmailSent = #EmailSent WHERE EmailSent IS NULL
Add a WHERE EmailSent = #EmailSent to your query for the email
You can accomplish this with a SSRS report with a subscription or data-bound subscription instead of this job, but it's really just a matter of preference. Formatting is easier in SSRS, otherwise you're messing with dynamic HTML in your message body. Been there, not pleasant.

How do I modify stored procedure so it can process request based on request mode?

We wrote a stored procedure that uses SQL Server's send_dbmail feature to send emails to potential users with details of their registrations.
This stored procedure works great.
The only issue we currently have is that users will also like to request a copy of their forgotten password.
I can write another stored procedure to handle this? However, is there a way to modify the stored procedure below so that it recognizes the mode of the request?
For instance, currently a user creates an account and details of the account are stored in tblLogin table.
This stored procedure grabs the account info, stores it in Notifications table and finally email details of this account to the user who just registered.
Similarly, we would like the same stored procedure to process when a user uses the Forgotten Password feature to request a password.
How do I modify the stored procedure send email based on what is being requested?
In other words, if the data stored in tblLogin table is for a new account creation, the stored procedure should grab it and send an email to user with details.
If data is for forgotten password, the stored procedure should only email that info as well.
Is this possible with the stored procedure below?
ALTER PROCEDURE [dbo].[GetRegistrationInfo]
AS
BEGIN
DECLARE Register_Cursor CURSOR FOR
SELECT
LoginId, FullName, email, Password
FROM
[tblLogin]
WHERE
ProcessedFlag = 'No'
ORDER BY
LoginId DESC
OPEN Register_Cursor
DECLARE #LoginId INT
DECLARE #fullname NVARCHAR(100)
DECLARE #email NVARCHAR(MAX)
DECLARE #password NVARCHAR(20)
-- Get the current MAX ID
DECLARE #mailID as INT
-- Start reading each record from the cursor.
FETCH Register_Cursor INTO #LoginId, #fullname, #email, #password
WHILE ##FETCH_STATUS = 0
BEGIN
--set #mailID = (SELECT max(mailID) from Notifications) Not needed; let's auto-genereate the id
INSERT INTO [Notifications] (mailContent, LoginId, FullName, email, Password, sender, Sent)
VALUES ('This is a computer generated email message.
Please DO NOT use the REPLY button above to respond to this email.
Dear '+#FullName+':
Thanks for registering to take the Training!
Below are details of your registration information:
Your UserName is: '+#email+'.
Your Password is: '+#password+'.
Once you have retrieved your login information, please click the link below to get back to Training login screen and begin to begin to enjoy the benefits of membership.
http://servername/training/
Regards,
The Registrations & Elections Office.', #LoginId, #FullName, #email, #Password, 'NoReply#serverdomain', 'No')
FETCH Register_Cursor INTO #LoginId, #FullName, #email, #password
END
CLOSE Register_Cursor
DEALLOCATE Register_Cursor
END
BEGIN
DECLARE MAIL_CURSOR CURSOR FOR
SELECT mailid, sender, mailcontent
FROM [Notifications]
WHERE Sent = 'No'
DECLARE #mail1 INT
DECLARE #sender NVARCHAR(100)
DECLARE #content1 NVARCHAR(4000)
OPEN MAIL_CURSOR
FETCH MAIL_CURSOR INTO #mail1, #sender, #content1
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #email = #email + ';' + Email
FROM [Notifications]
WHERE sent = 'No'
-- exec sp_send_cdontsmail #mail1, null,null,#content1,null
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'The Office',
#recipients = #email, -- your email
--#blind_copy_recipients = #email,
#subject = 'Your Account Details',
#body = #content1;
-- Update the record in Notifications table where Sent = 'No'.
UPDATE [Notifications]
SET Sent = 'Yes'
WHERE Sent = 'No' AND mailid = #mail1
UPDATE [tblLogin]
SET ProcessedFlag = 'Yes'
WHERE ProcessedFlag = 'No' AND LoginId = #LoginId
FETCH MAIL_CURSOR INTO #mail1, #sender, #content1
END
CLOSE MAIL_CURSOR
DEALLOCATE MAIL_CURSOR
END
Sorry if I’m missing something here but this looks like you only need one IF - ELSE statement that will send one message in one case and another message otherwise…