SQL Server email queue - sql

I have an orders table which is populated with new orders frequently
Orders table:
OrderID OrderName EmailAddress Status
-----------------------------------------------------
1 iphone test#gmail.co.uk New
2 samsung nw#gmail.com New
3 nexus f#gmail.com Approved
For every order line which has status = 'New', I would like to set up a job to run every 30 minutes send an email to those recipients.

As per my understanding, you need to schedule a job which send an email notification if order status is 'New'.
If so create the following stored procedure
CREATE PROCEDURE NewOrders_Job
AS
BEGIN
DECLARE #OrderName varchar(200), #EmailAddress varchar(200)
SELECT
#OrderName = ' You have a sale for '+[OrderName]+', at +'CONVERT(VARCHAR(10),GETDATE(),)'+'
,#EmailAddress = [EmailAddress]
FROM [Orders table]
WHERE
[Status] = 'New'
IF(ISNULL(#EmailAddress,'')<>'')
BEGIN
--mail
DECLARE
#profile_name VARCHAR(200)
,#recipients VARCHAR(MAX)
,#copy_recipients VARCHAR(MAX)
,#blind_copy_recipients VARCHAR(MAX)
,#body VARCHAR(MAX)
,#body_format VARCHAR(MAX)
,#subject VARCHAR(MAX)
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'Your_Profile_Name'
,#recipients = #EmailAddress
,#copy_recipients = 'youremail#domain.com'
,#blind_copy_recipients = 'admin#yourdomain.com'
,#body = #OrderName
,#body_format = 'HTML'
,#subject = 'You have a new sales Order';
END
END
now, create a new job which run's for 30 mins with above stored procedure

Related

SQL Cursor and Email

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

Send email in a tabular format using SQL Server database mail

I have created SSIS package which has 1 SQL task (stored procedure which returns list of below 4 columns. How will achieve the following table to be sent through email. Immediate Next task here what I would do is create another stored procdure which accepts the result set from 1st stored proc through another Sql task and 2nd stored proc contains msdb_SendDBemail call . Is there any better approach
Nice thread on stack exchange: Need to Send a formatted HTML Email via Database Mail in Sql Server 2008 R2
It has an answer with a code of stored procedure dbo.HtmlTable that can generate HTML for further use in database mail:
CREATE table ##foo (bar1 int, bar2 varchar(20), bar3 datetime)
INSERT into ##foo values (1, 'Abcdef', getdate())
INSERT into ##foo values (2, 'Ghijkl', '05/05/15')
DECLARE #tableHtml varchar(max)
EXEC dbo.HtmlTable
'##foo',
#tableHtml output
PRINT #tableHtml
#tableHtml will contain html that renders into:
You can try something like this to get data in tabular format. To execute row by row you need to implement a cursor like this.
CREATE PROCEDURE [dbo].[SendNewDeviceInfoToEmployee]
AS
BEGIN
SET NOCOUNT ON
DECLARE #Mbody VARCHAR(MAX),
#Sub VARCHAR(500),
#EmpEmailPwdId BIGINT,
#Emp_nm VARCHAR(150),
#ToEmailid VARCHAR(150),
#NewEmailId VARCHAR(150),
DECLARE #Recipients VARCHAR(max)
DECLARE SendautoLoginDtl CURSOR FOR
SELECT EmpEmailPwdId, EmpName, ToEmailid, NewEmailId, EmpPassword
FROM EmployeeEmailIdPassword WHERE ISNULL(Freeze,'N') = 'N' AND ISNULL(IsMailSent,'N') = 'N' AND ISNULL(ToEmailId,'') <> '' AND ISNULL(NewEmailId,'') <> ''
ORDER BY EmpEmailPwdId
OPEN SendautoLoginDtl;
FETCH NEXT FROM SendautoLoginDtl INTO #EmpEmailPwdId, #emp_nm, #ToEmailid, #NewEmailId
WHILE( ##FETCH_STATUS = 0 )
BEGIN
SET #xml = CAST(( SELECT [Rank] AS 'td','',[Player Name] AS 'td','', [Ranking Points] AS 'td','', Country AS 'td'
FROM #Temp
ORDER BY Rank
FOR XML PATH('tr'), ELEMENTS ) AS NVARCHAR(MAX))
SET #Mbody =N'<html><body><H3>Tennis Rankings Info</H3>
<table border = 1>
<tr>
<th> Rank </th> <th> Player Name </th> <th> Ranking Points </th> <th> Country </th></tr>'
SET #body = #body + #xml +'</table></body></html>'
SET #sub='Your New Device Details - ' + #emp_nm
set #Recipients = #ToEmailid
EXEC msdb.dbo.sp_send_dbmail '<YourDBMailProfileName>' ,
#recipients = #Recipients ,
#copy_recipients = #NewEmailId,
#subject = #sub ,
#body = #Mbody ,
#body_format = 'HTML'
--Update table if required
UPDATE <YourTable>
SET IsMailSent = 'Y'
WHERE EmpEmailPwdId = #EmpEmailPwdId
FETCH NEXT FROM SendautoLoginDtl INTO #EmpEmailPwdId, #emp_nm, #ToEmailid, #NewEmailId, #EmpPassword
END
CLOSE SendautoLoginDtl;
DEALLOCATE SendautoLoginDtl;
END
You can learn about cursor here.

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.

SQL send db mail to multiple recipients with each related data

I've created a two stored procedures, one holding the data info and the other is more for the call of db mail. I have a samples of the second stored procedure below as the issues is there.
BEGIN
SET NOCOUNT ON;
declare #Coord_ID varchar(20)
declare #Coord_Name varchar(25)
declare #Coord_Email varchar(75)
declare #CoordCursor as cursor
declare #tab char(1) = CHAR(9)
declare #emailBody varchar(100)
declare #sqlCommand varchar(1000) = 'sp_MZ_Get'
declare #qry varchar(1000)
set #CoordCursor = cursor Fast_Forward for
select distinct TARGET_ID, FULL_NAME, Email from vw_Coords where Email <> '' --and Target_ID = '7000001'
open #CoordCursor
fetch next from #CoordCursor into #Coord_ID,#Coord_Name, #Coord_Email
while ##FETCH_STATUS = 0
BEGIN
Print #Coord_ID + ' ' + #Coord_Email
set #emailBody = 'Member Renewal Transaction Activity for '+ #Coord_Name
DECLARE #RenewalActivity TABLE
(
Membership_Type varchar(25),
Member_ID varchar(20),
Chapter varchar(20),
Full_Name varchar(35),
Transaction_Date smalldatetime,
Renewal_Date smalldatetime,
AC_ID varchar(10),
AC_Name varchar(30)
)
insert into #RenewalActivity(Membership_Type, Member_ID, Chapter, Full_Name, Transaction_Date, Renewal_Date, AC_ID, AC_Name)
exec #sqlCommand #Coord_ID
if OBJECT_ID('tempdb..##temprenewals') is not null
drop table ##temprenewals
select * into ##temprenewals from #RenewalActivity
set #qry = 'select * from ##temprenewals'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'Admin',
#recipients = #Coord_Email,
#query = #qry,
#execute_query_database = 'TOPS',
#subject = 'New Members and Renewals',
#body = #emailBody,
#attach_query_result_as_file = 1,
#query_attachment_filename='coord-s-9.csv',
#query_result_separator=#tab,
#query_result_no_padding=1
fetch next from #CoordCursor into #Coord_ID,#Coord_Name, #Coord_Email
END
close #CoordCursor
deallocate #CoordCursor
1st issue; the recipients receive the report contains all the data. as they should receive just what it related to them
example:
Member_ID | Chapter |Coord_Email
--------------------------------------
1 | A05 | Coord2#Email.com
5 | A01 | Coord1#Email.com
8 | A05 | Coord2#Email.com
12 | A01 | Coord1#Email.com
Currently 1,5,8 and 12 go to Coord1 and Coord2. the intention is 5 and 12 associated with A01 go to Coord1 and 1 and 8 go to Coord2.
2nd issue; the db mail is successfully queued as it stated in the log with 35 recipients. However, not all the recipients was included in the mail. Out of 90 recipients only 35 received the emails. i checked the 1st query and it resulted all the recipients "90".
Thank you in advance

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