SQL Cursor and Email - sql

I have a cursor in place that needs to send an email to one person containing all the rows associated with a certain field
To explain a bit further, i have a table which contains multiple rows, with one field containing the email address for all the rows associated with that email address.
I'm looking to send one email for that user with all the rows associated with that email address. At the moment, so if it has 4 rows, sends one email with all those rows. What its doing at the moment is sending 4 emails with all the rows associated with that email address
Essentially, just need it to send one email with all the rows for that email address
Thanks
Cursor loops through
Updates a table
Email then uses the variables to send the email
DECLARE #SUBJECT VARCHAR(100)
DECLARE #BODY VARCHAR(MAX)
DECLARE #EMAIL VARCHAR(60)
DECLARE #SUPERVISOREMAIL VARCHAR(60)
DECLARE #TABLEHTML VARCHAR(MAX)
DECLARE #TPROFILE VARCHAR(128)
DECLARE #ASSETREF VARCHAR(MAX)
DECLARE #PERSONREF VARCHAR(MAX)
DECLARE #USERNAME VARCHAR(MAX)
DECLARE #UPDATEDINVFIRE BIT
DECLARE #DATEUPDATEDINVFIRE DATETIME
DECLARE #DEVICEMODEL VARCHAR(MAX)
DECLARE #DESCRIPTION VARCHAR(MAX)
DECLARE CSR CURSOR
FOR SELECT ASSETREF, USERNAME, EMAIL, SUPERVISOREMAIL, UPDATEDINVFIRE, DATEUPDATEDINVFIRE FROM ##LEAVERUPDATE
OPEN CSR FETCH NEXT
FROM CSR INTO #ASSETREF, #USERNAME, #EMAIL, #SUPERVISOREMAIL, #UPDATEDINVFIRE, #DATEUPDATEDINVFIRE
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SUBJECT = 'VFIRE DEVICE STATUS UPDATED FOR LEAVER'
SET #TABLEHTML = N'<P> The following user has had the following devices set to leaver status in vFire</P>' +
N'<P> Please be advised, these devices need to be returned immediately prior to the person leaving the business</P>' +
N'<table border="1">' +
N'<tr><th>CI Number</th><th>Username</th><th>Asset Model</th><th>Asset Description</th><th>Users Email Address</th><th>Supervisor EMail Address</th></tr>' +
CAST
((
SELECT
TD = ASSETREF, '',
TD = USERNAME, '',
TD = DEVICEMODEL, '',
TD = DESCRIPTION,'',
TD = EMAIL, '',
TD = SUPERVISOREMAIL,''
FROM ##LEAVERUPDATE
WHERE EMAIL = #EMAIL
FOR XML PATH('TR'), TYPE
) AS NVARCHAR(MAX) ) +
N'</TABLE>' +
N'<P> </P>';
EXEC msdb.dbo.sp_send_dbmail
#profile_name = #TPROFILE,
#recipients = #SUPERVISOREMAIL,
#copy_recipients='',
#blind_copy_recipients ='',
#subject = #SUBJECT,
#body = #TABLEHTML,
#body_format = 'HTML'
------------------------------------------
FETCH NEXT
FROM CSR INTO #ASSETREF, #USERNAME, #EMAIL, #SUPERVISOREMAIL, #UPDATEDINVFIRE, #DATEUPDATEDINVFIRE
END
CLOSE CSR
DEALLOCATE CSR

Related

How to send an email only to users who appear in query results?

I am creating a query to return tickets that haven't been updated in the required time and send via email. I have everything working except I want to email only the users whose names appear in the query results.
I have tried setting the # user name = to user id and adding to the email as below. Not sure if this is even close.
Declare #username VARCHAR(MAX)
--("query")--
Set #username =[userid]--(in query results)--
exec msdb.dbo.sp_send_dbmail
#participants ='#username+#mydomain.com'
Expect to send emails to users whose user name appears in query results.
You can use STUFF with FOR XML PATH to create a ; delimited list for the #recipients parameter:
DECLARE #EmailList varchar(max)
SELECT #EmailList = STUFF((
SELECT ';' + Username + '#gmail.com'
FROM yourTable
FOR XML PATH('')
), 1, 1, '')
Then:
EXEC msdb.dbo.sp_send_dbmail
#recipients = #EmailList,
#subject = 'foo',
#body = 'bar',
etc
You can use Coalesce to create a list separated with semicolons which is what dbmail wants.
Declare #username varchar(max)
SELECT
#username = COALESCE(#username + '; ','') + userid + '#mydomain'
FROM
Yourtable
WHERE
Yourquery
EXEC msdb.dbo.sp_send_dbmail
#recipients = #username
#subject = 'BARK BARK'
#Body = 'BARK BARK BARK'
You can use a CURSOR to fetch your email address list and send the email to that list:
DECLARE #username AS VARCHAR(MAX) = ''
DECLARE #participants AS VARCHAR(MAX) = ''
DECLARE cursor_name CURSOR
FOR
SELECT userid
FROM YourTable
OPEN cursor_name;
FETCH NEXT
FROM cursor_name
INTO #username;
WHILE ##FETCH_STATUS = 0
BEGIN
#participants += #username + '#mydomain.com;'
FETCH NEXT FROM cursor_name
INTO #username;
END
CLOSE cursor_name;
DEALLOCATE cursor_name;
EXEC msdb.dbo.sp_send_dbmail #recipients = #participants
etc

how I can resend all failed emails (msdb.dbo.sp_send_dbmail)

I am sending emails using msdb.dbo.sp_send_dbmail.
Sometimes the emails do not send. I have got the list of emails which have failed.
SELECT TOP 10 * from msdb.dbo.sysmail_event_log
log_id event_type log_date process_id mailitem_id account_id last_mod_date last_mod_user
9022 error 50:15.9 5608 20428 NULL 50:15.9 sa
9023 error 51:23.3 5608 20428 NULL 51:23.3 sa
Now, I want to resend all failed emails again which are available in the sysmail_event_log table.
How can I resend all failed emails?
Use the following query for sending back a failed item.
Or use a CURSOR for each row from msdb.dbo.sysmail_faileditems with the same query
DECLARE #to varchar(max)
DECLARE #copy varchar(max)
DECLARE #title nvarchar(255)
DECLARE #msg nvarchar(max)
SELECT #to = recipients, #copy = copy_recipients, #title = [subject], #msg = body
FROM msdb.dbo.sysmail_faileditems
WHERE mailitem_id = 56299
EXEC msdb.dbo.sp_send_dbmail
#recipients = #to,
#copy_recipients = #copy,
#body = #msg,
#subject = #title,
#body_format = 'HTML';
References
resending-failed-emails-through-sp_send_email
CURSOR
Building from Hybris95's answer, here is a snippet that sends all failed items after a cutoff timestamp without a cursor. Mail profiles are taken in consideration.
DECLARE #MailitemId INT = 0
DECLARE #Cutoff DATE = CAST(GETDATE() AS DATE)
WHILE (1 = 1)
BEGIN
SELECT TOP 1 #MailitemId = mailitem_id
FROM msdb.dbo.sysmail_faileditems
WHERE
mailitem_id > #MailitemId
AND send_request_date > #Cutoff
ORDER BY mailitem_id
IF ##ROWCOUNT = 0 BREAK;
DECLARE #to VARCHAR(MAX)
DECLARE #copy VARCHAR(MAX)
DECLARE #title NVARCHAR(255)
DECLARE #msg NVARCHAR(MAX)
DECLARE #profile_name NVARCHAR(MAX),
#file_attachments NVARCHAR(MAX),
#attach_query_result_as_file BIT
SELECT #profile_name = p.name, #to = recipients, #copy = copy_recipients, #title = [subject], #msg = body, #file_attachments = i.file_attachments, #attach_query_result_as_file = i.attach_query_result_as_file
FROM msdb.dbo.sysmail_faileditems AS i LEFT OUTER JOIN msdb.dbo.sysmail_profile AS p
ON p.profile_id = i.profile_id
WHERE
mailitem_id = #MailitemId
EXEC msdb.dbo.sp_send_dbmail
#profile_name = #profile_name,
#recipients = #to,
#copy_recipients = #copy,
#body = #msg,
#subject = #title,
#body_format = 'HTML',
#file_attachments = #file_attachments,
#attach_query_result_as_file = #attach_query_result_as_file
END
Or you can do what send_dbmail itself does after building the message in source tables (which still exists in case of failed mail), and just push the failed message back onto the service broker send queue, and let it manage all the correct updates.
Declare #sendmailxml VARCHAR(max), #mailitem_id INT
-- Create the primary SSB xml maessage
SET #sendmailxml = '<requests:SendMail xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.microsoft.com/databasemail/requests RequestTypes.xsd" xmlns:requests="http://schemas.microsoft.com/databasemail/requests"><MailItemId>'
+ CONVERT(NVARCHAR(20), #mailitem_id) + N'</MailItemId></requests:SendMail>'
-- Send the send request on queue.
EXEC sp_SendMailQueues #sendmailxml
where #mailitem_id is the id of the message to resend that you have identified.
Just be aware the exact format of the XML Message is an undocumented internal, so may change by SQL version. The indented lines (including misspelled comment!) are pulled straight out of sp_send_dbmail on a SQL 17 server, and are same on 19. I believe they were different on some earlier version, but don't have installs to check.

Unable to develop body for email in SQL

I have written a stored procedure where "i have to compare values in two tables and send mail to the users which are not present in second table.
I have to automate this report .
But issue comes when i am trying to make mail body for the same.
The mail body is to be taken from table record ie column name REQ_Approval_EMAILBODY.Also the Recipient id has to be taken from table records ie cloumn name REQ_Approval_EMAILID.
This query has to execute for all the records one by one with respective email ID from table.
Please help me to sort out his.
Thanks in advance
CREATE PROCEDURE Email_Daily
AS
BEGIN
DECLARE #bodynew VARCHAR(Max)
DECLARE #query1 VARCHAR(Max)
DECLARE #Para VARCHAR(Max)
DECLARE #query2 VARCHAR(Max)
DECLARE #query3 VARCHAR(Max)
DECLARE #query5 VARCHAR(Max)
DECLARE #Yesterday VARCHAR(50)
SET #Yesterday = REPLACE(CONVERT(varchar(50),GETDATE()-1,102),'.','-')
DECLARE #TODAY_DDMMYYY VARCHAR(50)
SET #TODAY_DDMMYYY = REPLACE(CONVERT(varchar(50),GETDATE()-1,6),' ','-')
SELECT * into #Temp3_New_Table
FROM(
SELECT [MessageIdentifier]
,[MessageIdentifier_Archive]
,CONVERT(DATE,REQ_SentDate) AS REQ_SentDate
,[REQ_Token]
,[REQ_Approval_Number]
,[REQ_Order_Number]
,[REQ_Document_Number]
,[REQ_Approval_EMAILID]
,[REQ_Approval_EMAILBODY]
,[REQ_Approval_EMAIL_Subject]
,[RES_Date]
,[IsValid]
,[Response_MessageIdentifier]
,[ReSubmit_Count]
,'0' as Flag
FROM [SAEI].[dbo].[Approval_EMAIL_LOG]
where CONVERT(DATE,REQ_SentDate) = #Yesterday
and res_date is null
and req_approval_number+req_order_number+req_document_number not in
(SELECT
[RES_Approval_Number]+[RES_Order_Number]+[RES_Document_Number]
FROM [SAEI].[dbo].[Approval_Response_WEB_LOG]))as Tr
set #query5='select * from #Temp3_New_Table'
set #para='<p style="font-family:arial; font-size:14px">
Dear Team,
</br>
</br>
</br>
</p>'
Set #bodynew = #PARA
DECLARE #subjects VARCHAR(200)
DECLARE #FileAttachment VARCHAR(400)
DECLARE #Recipients VARCHAR(MAX)
DECLARE #copy VARCHAR(MAX)
set #Recipients='select [REQ_Approval_EMAILID] from #Temp3_New_Table'
drop table #Temp3_New_Table
End
try this
select #para = concat('<p style="font-family:arial; font-size:14px">Dear Team,</br></br>', [REQ_Approval_EMAILBODY],'</br></p>')
from #Temp3_New_Table
{where condition}
EDIT
if you want a cursor then you can try this
DECLARE evilCurse CURSOR FOR
SELECT [REQ_Approval_EMAILID]
,[REQ_Approval_EMAILBODY] = concat('<p style="font-family:arial; font-size:14px">Dear Team,</br></br>', [REQ_Approval_EMAILBODY],'</br></p>')
,[REQ_Approval_EMAIL_Subject]
FROM [SAEI].[dbo].[Approval_EMAIL_LOG]
where CONVERT(DATE,REQ_SentDate) = #Yesterday
and res_date is null
and req_approval_number+req_order_number+req_document_number not in
(SELECT [RES_Approval_Number]+[RES_Order_Number]+[RES_Document_Number]
FROM [SAEI].[dbo].[Approval_Response_WEB_LOG])
OPEN evilCurse
FETCH NEXT FROM evilCurse
INTO #Recipients, #bodynew, #subjects
WHILE ##FETCH_STATUS = 0
BEGIN
--do what ever you want to do to here
FETCH NEXT FROM evilCurse
INTO #Recipients, #bodynew, #subjects
END
CLOSE evilCurse;
DEALLOCATE evilCurse;

Using sp_send_dbmail with recieptent and content from single sql row

I have a table filled with oldUserID, newUserID, name and email. I want to use sp_send_dbmail to the email on each row. For example:
oldUserID | newUserID | name | email
21213125 | 2355233571 | Tom | tom#gmail.com
65465465 | 4564884664 | Mat | mat#gmail.com
And so on for 200 rows. Is there any way to send an sp_send_dbmail to the email on each row including oldUserID and newUserID? The output in the mail would be something like:
"Your old user id: 21213125, your new user id: 2355233571"
I would appreciate not to enter each emailadress manually.
Thank you!
DECLARE
#txt NVARCHAR(MAX)
, #name NVARCHAR(60)
, #email VARCHAR(100)
DECLARE cur CURSOR FAST_FORWARD READ_ONLY LOCAL FOR
SELECT 'Your old user id: ' + CAST(oldUserID AS NVARCHAR(100))
+ ', your new user id: ' + CAST(newUserID AS NVARCHAR(100)), name, email
FROM ...
OPEN cur
FETCH NEXT FROM cur INTO #txt, #name, #email
WHILE ##fetch_status = 0
BEGIN
EXEC msdb.dbo.sp_send_dbmail #profile_name = ...
, #recipients = #email
, #subject = #name
, #body = #txt
, #body_format = 'HTML'
FETCH NEXT FROM cur INTO #txt, #name, #email
END
CLOSE cur
DEALLOCATE cur

Stored procedure sends email even when condition is false. Any ideas?

When a user uses our site to process certain requests, upon exiting the site, an email with a link goes out to that user asking him/her to click the link to take a survey and share his/her user experiences with us.
Below is the stored procedure that I have written that does as described above.
ALTER proc [dbo].[SendSurvey]
AS
BEGIN
Declare #sender nvarchar(200)
declare #dept nvarchar(200) = ''
declare #loc nvarchar(200) = ''
declare #dteCreated nvarchar
declare #RequestID nvarchar(50) = ''
declare #authorizedname nvarchar(200) = ''
declare #email nvarchar(200) = ''
declare #message nvarchar(1000) = ''
declare #mailid int = 0
SET QUOTED_IDENTIFIER OFF;
SELECT
#email = email, #mailid=ID, #message = #message,
#RequestID = RequestID,
#authorizedname = SUBSTRING(submittedBy, CHARINDEX(',', submittedBy) + 1, LEN(submittedBy) - CHARINDEX(',', submittedBy) + 1)
+ ' ' + SUBSTRING(submittedBy, 1, CHARINDEX(',', submittedBy) - 1),
#loc = Bldg, #dtecreated = DateCreated, #dept = Department
FROM
Survey
WHERE
email = #email
AND Email IS NOT NULL OR Email != ''
AND (orderStatus != 1)
SELECT #message = 'This is a computer generated email message.
Please DO NOT use the REPLY button above to respond to this email.
Dear '+ #authorizedname +':
Thank you for using the order processing system.
Please click the link below to complete a survey
http://feedbacksurvey.php?rID=' +#RequestID+'&loc='+Replace(#loc,' ', '%20')+'&dept='+Replace(#dept,' ', '%20')+'
Regards,
web admin.'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'Customer Feedback Survey',
#recipients = #Email, -- your email
#subject = 'Customer Feedback Survey',
#body = #message;
UPDATE Survey
SET orderStatus = 1
WHERE orderStatus != 1 AND ID = #mailid
END
There are two problems with the stored procedure.
There is a column orderStatus which is a BIT data type with True (1) of false(0) value.
If the orderstatus is false, then send emails with records associated with it.
After sending the email, update orderstatus to true so the email doesn't get sent a second time.
This is not working. When I execute the stored procedure where all records on the table have orderstatus set to True, email still goes out.
the second problem that I am having is that the code is not sending out all records where orderStatus is True. It just sends email one at a time.
We would like emails to be send out for ALL records where orderstatus = 1 (True).
Any ideas what I am doing wrong?
You mixed AND with OR in your WHERE clause. The results will include all rows where Email != '', regardless of the other conditions.
Use parens to make this work:
WHERE email=#email
AND (Email IS NOT NULL or Email != '')
AND (orderStatus != 1)
As for why it's sending one email at a time, you are using your query to populate scalar variables.
SELECT #email = email...
Will result in #email being populated with one value, no matter how many rows the query returns.
the second problem that I am having is that the code is not sending out all records where orderStatus is True. It just sends email one at a time.
Yeah - that's how sp_send_dbmail works. You'll need a cursor to send > 1 email.
DECLARE c CURSOR LOCAL FAST_FORWARD READ_ONLY FOR
SELECT ....
FETCH NEXT FROM c INTO ....
WHILE ##FETCHSTATUS = 0 BEGIN
EXEC sp_send_dbmail ...
FETCH NEXT FROM c INTO ....
END
CLOSE c
DELLOCATE c