Send Data Via Email in T-SQL - sql

I am writing a stored procedure which collects all changes that were happend in a table for a given date and would like to send recordset to an administrator via email from SQL Batch Job.
Now untill now, I couldn't figure out is how to send recordset in an email body. Is it possible to construct email body based on the recordset obtained from some tsql logic.
Any help is appriciated
Thanks
TheITGuy

Look into the sp_send_dbmail procedure, which allows you to execute a query and send the results in the e-mail body or as a file attachment.

This might provide you some impetus:
CREATE TABLE #Temp
(
[Rank] [int],
[Player Name] [varchar](128),
[Ranking Points] [int],
[Country] [varchar](128)
)
INSERT INTO #Temp
SELECT 1,'Rafael Nadal',12390,'Spain'
UNION ALL
SELECT 2,'Roger Federer',7965,'Switzerland'
UNION ALL
SELECT 3,'Novak Djokovic',7880,'Serbia'
DECLARE #xml NVARCHAR(MAX)
DECLARE #body NVARCHAR(MAX)
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 #body ='<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>'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'SQL ALERTING', -- replace with your SQL Database Mail Profile
#body = #body,
#body_format ='HTML',
#recipients = 'bruhaspathy#hotmail.com', -- replace with your email address
#subject = 'E-mail in Tabular Format' ;
DROP TABLE #Temp

The firm I work for requires the name/value pair to be on separate lines rather than in a table. While my solution could use some tuning, after much work, I came up with a solution with css formatting. I can't figure out why, however, the width of the NAME column isn't 100px, as I have requested.
The special characters are < alt >+0182, < alt >+0183 and < alt >+0185, respectively.
I will continue to strive for a more elegant solution without so many replace functions but the firm is more concerned with a 'now' solution than an elegant one. Feedback/improvements would be appreciated.
The email arrives (in Outlook) formatted as such, which is our firm's preference:
Tennis Rankings Info
Rank: 1
Player Name: Rafael Nadal
Points: 12390
Country: Spain
Rank: 2
Player Name: Roger Federer
Points: 7965
Country: Switzerland
Rank: 3
Player Name: Novak Djokovic
Points: 7880
Country: Serbia
Here is my solution:
CREATE TABLE #Temp
(
[Rank] [int],
[Player Name] [varchar](128),
[Ranking Points] [int],
[Country] [varchar](128)
)
INSERT INTO #Temp
SELECT 1,'Rafael Nadal',12390,'Spain'
UNION ALL
SELECT 2,'Roger Federer',7965,'Switzerland'
UNION ALL
SELECT 3,'Novak Djokovic',7880,'Serbia'
DECLARE #xml NVARCHAR(MAX)
DECLARE #body NVARCHAR(MAX)
SET #xml = CAST(( SELECT '¶Rank:·º' + convert(varchar, [Rank]) AS 'br','','¶Player Name:·º' +[Player Name] AS 'br','',
'¶Points:·º' + convert(varchar, [Ranking Points]) AS 'br','', '¶Country:·º' + Country AS 'br'
FROM #Temp ORDER BY Rank
FOR XML PATH('br'), ELEMENTS ) AS NVARCHAR(MAX))
SET #body ='<html><head><style type=''text/css''>body{font-family:tahoma;font-size:9pt;}.h{width:100px;}</style></head><body><H3>Tennis Rankings Info</H3>'
SET #body = #body + #xml +'</body></html>'
set #body = replace(#body, '</br>', '<br />')
set #body = replace(#body, '<br><br>', '<p>')
set #body = replace(#body, '<br>', '')
set #body = replace(#body, '<br /><br />', '</p>')
set #body = replace(#body, '¶', '<span class=''h''>')
set #body = replace(#body, '·', '</span>')
set #body = replace(#body, 'º', ' ')
--select #body
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'DBMail', -- replace with your SQL Database Mail Profile
#body = #body,
#body_format ='HTML',
#recipients = 'xxx#xxx.com', -- replace with your email address
#subject = 'E-mail in Tabular Format' ;
DROP TABLE #Temp

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

stored procedure not running, maybe bad job configuration

i have a stored procedure, on a sql server, with the following code:
USE [MASS]
GO
/****** Object: StoredProcedure [dbo].[DossierEmailSend] Script Date: 05/11/2019 17:36:13 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[DossierEmailSend]
AS
--Create a holding table for the dossiers that met the criteria
DECLARE #dossiers TABLE (col1 varchar(100), col2 varchar(100), col3 varchar(100), col4 varchar(100));
--Insert the dossiers that met the criteria
INSERT INTO #dossiers
SELECT no, nome, obrano, convert(varchar,datafinal) col4
FROM bo
WHERE nmdos LIKE '%preço%'
AND datafinal = DATEADD(day, -1, CONVERT(date, GETDATE()))
--Create a row check to determine whether to send the email or not
DECLARE #rows int;
SET #rows = (SELECT COUNT(*) FROM #dossiers)
--Set the body elements
DECLARE #message varchar(1000);
-- declare the xml data to pass to the HTML body
DECLARE #xml NVARCHAR(MAX);
-- body will hold the HTML formatted table in the email
DECLARE #body NVARCHAR(MAX);
--Create the columns that will hold each row of data as xml
SET #xml = CAST(( SELECT col1 AS 'td','',col2 AS 'td','', col3 AS 'td','', col4 AS 'td'
FROM #dossiers
FOR XML PATH('tr'), ELEMENTS ) AS NVARCHAR(MAX))
--Set the HTML for the body
SET #body ='<html><body><H3>Dossier Info</H3>
<table border = 1>
<tr>
<th> Num </th> <th> Nome </th> <th> Dossier </th> <th> Data de Fecho </th></tr>'
--Stitch everything together, appending the HTML table
SET #body = #body + #xml +'</table></body></html>'
SET NOCOUNT ON
--Check if any dossiers met the criteria
IF #rows > 0
BEGIN
--Send the email and append the data table to the body
EXEC dbo.uspSendEmail 'Encerramento Dossier Preços', 'ssantos#mass.pt', #body, NULL, 'filipeferreira#mass.pt'
SET NOCOUNT OFF
END
to run this procedure, i created a job, that fetches this procedure, but the fact is that it's not running. on the job properties, i have set:
on success action quit the job reporting success and on failure action the same thing. i really don't know if the mistake is here, any thoughts on this guys?

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

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.

SQL Server 2005 Database Mail Failures

I am running database mail on a SQL 2005 box. Occasionally mails fail to send, by quering the msdb.dbo.sysmail_mailitems table i can see there are items with a sent_status of "2", which is failed. I can query the sysmail_faileditems table to list all failed mails.
Is there anyway i can process/re-send these failed mail's?
Would it be reasonable to create a daily job to query this table looping through using a CURSOR to re-send the mails one by one, and then delete them from the table one by one.
If you have a better suggestion / ideas then please let me know.
Many thanks Karl
First up, i suggest you query faileditems to determine your main cause of failure:
SELECT items.subject ,
items.last_mod_date ,
l.description
FROM dbo.sysmail_faileditems AS items
INNER JOIN dbo.sysmail_event_log AS l ON items.mailitem_id = l.mailitem_id
If it's nothing that can be easily fixed, you can re-send them by looping through the sysmail_mailitems table and re-sending them based on the failure type (timeouts etc) in the faileditems log - some good examples in the suggestions of this blog: http://justgeeks.blogspot.co.uk/2007/05/resending-sysmail-emails.html
My personal favourite:
CREATE PROCEDURE sysmail_resend_timeout
AS
BEGIN
SET NOCOUNT ON
DECLARE SYSMAIL_LOG_RESEND_CURSOR CURSOR READ_ONLY
FOR
SELECT DISTINCT
l.mailitem_id ,
p.name ,
m.recipients ,
m.subject ,
m.body_format ,
m.body
FROM msdb.dbo.sysmail_log l WITH ( NOLOCK )
JOIN msdb.dbo.sysmail_mailitems m WITH ( NOLOCK ) ON m.mailitem_id = l.mailitem_id
JOIN msdb.dbo.sysmail_profile p WITH ( NOLOCK ) ON p.profile_id = m.profile_id
WHERE l.event_type = 3
AND m.sent_status = 2
AND l.description LIKE '%The operation has timed out%'
ORDER BY l.mailitem_id
OPEN SYSMAIL_LOG_RESEND_CURSOR
WHILE ( 1 = 1 )
BEGIN
DECLARE #mailitem_id INT ,
#profile_name NVARCHAR(128) ,
#recipients VARCHAR(MAX) ,
#subject NVARCHAR(255) ,
#body_format VARCHAR(20) ,
#body NVARCHAR(MAX)
FETCH NEXT FROM SYSMAIL_LOG_RESEND_CURSOR INTO #mailitem_id, #profile_name, #recipients, #subject, #body_format, #body
IF NOT ##FETCH_STATUS = 0
BEGIN
BREAK
END
PRINT CONVERT(VARCHAR, GETDATE(), 121) + CHAR(9) + CONVERT(VARCHAR, #mailitem_id) + CHAR(9) + #recipients
EXEC msdb.dbo.sp_send_dbmail
#profile_name = #profile_name ,
#recipients = #recipients ,
#subject = #subject ,
#body_format = #body_format ,
#body = #body
UPDATE msdb.dbo.sysmail_mailitems
SET sent_status = 3
WHERE mailitem_id = #mailitem_id
END
CLOSE SYSMAIL_LOG_RESEND_CURSOR
DEALLOCATE SYSMAIL_LOG_RESEND_CURSOR
END
GO
I know it's not really the answer you want to hear, but I always try and decouple the mail feature. I might use a trigger to spawn an external process if the mail sending needs to be timely, but I let the external script do the actual job of sending the mail. That way transient connection errors are taken care of by the MTA, and I don't have to worry about special book-keeping algorithms.