TSQL Emailing data field containing URL didn't work - html

I am trying to email data field containing URL. What am I doing wrong. Below is my code:
If I comment out the URL field [URL_Field] it works fine. Right now with the [URL_Field] the email sends but nothing comes through my email inbox.
DECLARE
#EmailSubject VARCHAR(100),
#TextTitle VARCHAR(100),
#TableHTML NVARCHAR(MAX),
#Body NVARCHAR(MAX),
#Profile_name VARCHAR(100) = 'TstDBMail',
#Recipients VARCHAR(2000) = 'name#company.com',
#startdate DATETIME,
#enddate DATETIME
SET #EmailSubject = 'ALERT: on'''+##SERVERNAME +''''
SET #TextTitle = 'Alert on ' + ##SERVERNAME +'.'
SET #TableHTML =
'<html>'+
'<head><style>'+
-- Data cells styles font size
'td {border:1px solid #ddd;padding-left:5px;padding-right:5px;padding-top:1px;padding-bottom:1px;font-size:10pt}'+
'</style></head>'+
'<body>'+
-- Text Title style
'<div style="margin-top:15px; margin-left:15px; margin-bottom:15px; font-weight:bold; font-size:13pt; font-family:calibri;">' + #TextTitle +'</div>' +
-- Color and columns names
'<div style="font-family:Calibri; "><table>'+'<tr bgcolor="red">'+
'<td align=left><font face="calibri" color=White><b>customer_id</b></font></td>'+
'<td align=left><font face="calibri" color=White><b>timestamp_utc</b></font></td>'+
'<td align=left><font face="calibri" color=White><b>URL_Field</b></font></td>'+
'</tr></div>'
-----------------------------------------------------------
----- Querying Data --------
-----------------------------------------------------------
SELECT #Body =(SELECT top 1
td = customer_id
,td = timestamp_utc
,td = URL_Field
FROM LAMN_Temp with (nolock)
FOR XML RAW('tr'), ELEMENTS)
SET #Body = REPLACE(#Body, '<td>', '<td align=left><font face="calibri">')
SET #TableHTML = #TableHTML + #Body + '</table></div></body></html>'
SET #TableHTML = '<div style="color:Black; font-size:8pt; font-family:Calibri; width:auto;">' + #TableHTML + '</div>'
-------------------------------
----- Sending email -----------
-------------------------------
IF(#Body IS NOT NULL)
BEGIN
EXEC msdb.dbo.sp_send_dbmail
#Profile_name = #Profile_name,
#Recipients = #Recipients ,
#Body = #TableHTML,
#Subject = #EmailSubject,
#Body_format = 'HTML'
END;

NULL appended to any string value results in NULL, rather than appending an empty string. So if you have any missing values for the URL_Field, the entire #Body will end up as NULL. This fails the IF check and won't send email.
You can probably fix it by changing this:
,td = URL_Field
To this:
,td = coalesce(URL_Field,'')
This other way this might happen is if URL_Field adds enough size to the variable it overflows.
But I agree with the comment that this is a misuse of the SQL mail feature. You'll have much less pain and get much better results using a real mail processing service. It'll probably also cost less in the long run if you're properly accounting for your time spent maintaining this.

Related

Change the Colour of a value in a SQL Server DB Mail

I have created a DB Mail alert that sends out a HTML formatted table showing the pass or fail status of my SQL agents.
The alert works well, but the user has request an additional change where they want conditional formating on Pass/Fail status of the data, where failed status is to be highlighted RED and pass status to be highlighted GREEN.
Is this possible to achieve to in SQL using DB Mail and HTML formatting?
A few forums have stated that JavaScript may need to be used but I'm not sure if its possible to even us JS in SQL.
My code is below:
----------------------------------------------------------------------------
----------------------------------- Declare Variables ----------------------
----------------------------------------------------------------------------
Declare #email_body nvarchar(max)
Declare #email_profile_name nvarchar(max)
Declare #email_recipients nvarchar(max)
Declare #email_subject nvarchar(max)
Declare #tableHTML nvarchar(MAX) = ''
Declare #Style nvarchar(MAX) = ''
Declare #Textdate nvarchar(max)
DECLARE #crlf nvarchar = CHAR(13)+CHAR(10)
Declare #Server nvarchar(max)
----------------------------------------------------------------------------
--------------------------- Set Varriables (That should not change) --------
----------------------------------------------------------------------------
set #Server = ##SERVERNAME
set #Textdate = cast(getdate() as date)
Set #email_subject = #Textdate + ': Morning SQL Server Checks'
----------------------------------------------------------------------------
----------------------------------- Set Varriables (User Input) ------------
----------------------------------------------------------------------------
--Add other Email addresses as needed. Seperate with Semi-colon (;)
set #email_recipients = 'Email#Email.com'
----------------------------------------------------------------------------
----------------------------------- Create Temp Tables ---------------------
----------------------------------------------------------------------------
IF OBJECT_ID('tempdb..#Results') IS NOT NULL
DROP TABLE #Results
Create table #Results (
[Server] nvarchar(max),
[Entity] nvarchar(max),
[Status] nvarchar(max),
[Message] nvarchar(max),
[Type] nvarchar(max))
----------------------------------------------------------------------------
----------------------------------- Generate Data --------------------------
----------------------------------------------------------------------------
--Gets SQL Agent Results (Script Derived from here:
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/534cc5be-0021-4766-
9eef-92fea819e2e3/script-to-get-sql-server-agent-job-schedule-and-the-last-
run-status?forum=sqldatabaseengine)
Insert into #Results
SELECT ##SERVERNAME as 'Server Name'
,J.Name AS 'Job Name'
--,CASE J.Enabled WHEN 1 THEN 'Yes' WHEN 0 THEN 'No' END as 'Job
Enabled'
,CASE WHEN LASTRUN.run_status = 0 THEN 'Failed'
WHEN LASTRUN.run_status = 1 THEN 'Succeeded'
WHEN LASTRUN.run_status = 2 THEN 'Retry'
WHEN LASTRUN.run_status = 3 THEN 'Cancelled'
ELSE 'Unknown' END as 'Last Run Status'
,LASTRUN.message as 'Last Run Message'
,'SQL Agent Jobs' as 'Type'
FROM msdb.dbo.sysjobs J LEFT OUTER JOIN msdb.dbo.sysjobschedules JS ON
J.job_id = JS.job_id
LEFT OUTER JOIN msdb.dbo.sysschedules S ON JS.schedule_id = S.schedule_id
LEFT OUTER JOIN (SELECT J1.job_id
,J1.RUN_DURATION
,J1.run_date
,J1.run_time
,J1.message
,J1.run_status
FROM msdb.dbo.sysjobhistory J1
WHERE instance_id = (SELECT MAX(instance_id)
FROM msdb.dbo.sysjobhistory J2
WHERE J2.job_id = J1.job_id)) LASTRUN ON J.job_id = LASTRUN.job_id
where J.Enabled = 1 --Only check for agents that have been enabled
--------------------------------------------------------------------------
----------------------------------- Generate Email -------------------------
----------------------------------------------------------------------------
--Set Style (CSS)
SET #Style += +N'<style type="text/css">' + N'.tg {border-
collapse:collapse;border-spacing:0;border-color:#aaa;}'
+ N'.tg td{font-family:Arial, sans-serif;font-size:14px;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#aaa;color:#333;background-color:#fff;}'
+ N'.tg th{font-family:Arial, sans-serif;font-size:14px;font-weight:normal;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#aaa;color:#fff;background-color:#f38630;}'
+ N'.tg .tg-9ajh{font-weight:bold;background-color:#68cbd0}' + N'.tg .tg-hgcj{font-weight:bold;text-align:center}'
+ N'</style>';
--Set Email Output (HTML Layout)
Set #tableHTML += #Style + #tableHTML + N'<H1 style="color:Blue;">' +
#Textdate + ': Morning SQL Server Checks</H1>' +
N'<H2>' + #Server + '</H2>' +
N'<H3>Databases</H3>' +
+ N'<table class="tg">' --DEFINE TABLE
-- Define Headers for Database Check
+ N'<tr>'
+ N'<H3>SQL Agents</H3>' +
+ N'<table class="tg">' --DEFINE TABLE
+ N'<tr>'
+ N'<td class="tg-9ajh">Server</td>'
+ N'<td class="tg-9ajh">Entity</td>'
+ N'<td class="tg-9ajh">Status</td>'
+ N'<td class="tg-9ajh">Message</td>'
+ N'<td class="tg-9ajh">Type</td></tr>'
-- Define data for SQL Agent and cast to xml
+ Cast((
Select td = isnull([Server],'###')
,''
,td = isnull([Entity],'###')
,''
,td = isnull([Status],'###')
,''
,td = isnull([Message],'###')
,''
,td = isnull([Type],'###')
,''
from #Results
where [type] = 'SQL Agent Jobs' FOR
XML PATH('tr') ,
TYPE
) AS NVARCHAR(MAX))
+ N'</table>';
-- Send the Email
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'Outlook Support Profile',
#recipients = #email_recipients,
#body = #tableHTML,
#body_format = 'HTML',
#subject = #email_subject
----------------------------------------------------------------------------
----------------------------------- Cleanup --------------------------------
----------------------------------------------------------------------------
drop table #Results
I hope the code is clear enough and makes sense.
Thanks you
Anthony
I do something similar with my dbmail audit emails to differentiate between production and non-production instances.
The method I use is to predefine the style information and then use a unique value in the body string to identify where the style needs to be applied. In your case the unique values we can use are <td>Failed and <td>Succeeded. By doing a replace on those values to a version that includes the style, you should get a table with your conditional formatting.
I made the following two changes to your script and ran it against one of my test instances. It looks like it should get you where you want.
First, update the style section:
--Set Style (CSS)
SET #Style += +N'<style type="text/css">' + N'.tg {border-
collapse:collapse;border-spacing:0;border-color:#aaa;}'
+ N'.tg .green{background-color: green; color:white;}'
+ N'.tg .red{background-color: red; color:white;}'
+ N'.tg td{font-family:Arial, sans-serif;font-size:14px;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#aaa;color:#333;background-color:#fff;}'
+ N'.tg th{font-family:Arial, sans-serif;font-size:14px;font-weight:normal;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#aaa;color:#fff;background-color:#f38630;}'
+ N'.tg .tg-9ajh{font-weight:bold;background-color:#68cbd0}' + N'.tg .tg-hgcj{font-weight:bold;text-align:center}'
+ N'</style>';
After #tableHTML is built, add a REPLACE to shove the style formatting into the generated table:
SELECT #tableHTML = REPLACE(REPLACE(#tableHTML,N'<td>Failed',N'<td class="Red">Failed'),N'<td>Succeeded',N'<td class="green">Succeeded')

Aliasing rows with hyperlinks when sending HTML table via sp_send_dbmail

I am emailing an HTML table to users but I want to alias the hyperlinks for space reasons. Please have a look on the image.
Here is the Sample1 of the image:
Please see my below code :
DECLARE #bodyMsg nvarchar(max)
DECLARE #subject nvarchar(max)
DECLARE #tableHTML nvarchar(max)
SET #subject = 'Query Results in HTML with CSS'
DECLARE #email_to_notify nvarchar(256);
SELECT #email_to_notify = 'bens#ioec.co.za' ;
SET #tableHTML =
N'<style type="text/css">
...
</tr>' +
CAST ( (
SELECT
td = y.Customer,'',
td = y.ContractNumber,'',
td = y.Description ,'',
td = y.Market ,'',
td = y.BDM ,'',
td = y.EC ,'',
td = y.Reason ,'',
td = y.Link
FROM (SELECT FileReference AS 'Reference', Name AS 'Customer' ,
CASE WHEN ContractNumber IS NULL THEN '' ELSE ContractNumber END AS 'ContractNumber',
'http://gateway.ioec.co.za/#/trade/files/' + CONVERT(varchar(10),Gw_File.FileID) AS 'Link'
FROM Gw_File INNER JOIN ... ) y
FOR XML PATH('tr'), TYPE
) AS NVARCHAR(MAX) ) +
N'</table>'
EXEC msdb.dbo.sp_send_dbmail #recipients='rajivs#ioec.co.za',
#subject = #subject,
#body = #tableHTML,
#body_format = 'HTML' ;
I would also like to highlight rows depending on value of the reason column.
It seems like that when you're passing your markup, you aren't retaining it's type. Special characters, such as < will be replaced with a string representation to keep the xml valid. For example < & < and & & &.
If it's just the > and < symbols you can use a replace before sending out the email:
SET #tableHTML = REPLACE(REPLACE(#tableHTML,'>','<'),'<','>');
This however, trusts you have no nested HTML.

SQL HTML email has no content but data is present in table

I am sending a HTML email that contains some rows from a table. I am using a temporary table #tableupdate which definitely has data in it when I run the script.
Everything runs fine and I get the email, it just has no content whatsoever. When I view the source of the email it's blank. Can anyone see where I have gone wrong?
To help diagnose the problem, I've been outputting the contents of the variable containing all the HTML in it at various points and it seems to be empty after this part:
select #tableHTML = #tableHTML + #body
+ '</table></div></body></html>'
#body doesn't seem to populate at all. I'm just not sure what to change to make it work.
The relevant piece of code, including comments to show where the data disappears:
select * from #tableupdate --rows are returned
declare #emailSubject varchar(100),
#textTitle varchar(100),
#tableHTML nvarchar(max)
select #textTitle = 'Test table'
set #tableHTML = '<html><head><style>' +
'td {border: solid black 1px;padding-left:5px;padding-right:5px;padding-top:1px;padding-bottom:1px;font-size:11pt;} ' +
'</style></head><body>'
+ '<div style="margin-top:20px; margin-left:5px; margin-bottom:15px; font-weight:bold; font-size:1.3em; font-family:tahoma;">' +
#textTitle + '</div>'
+ '<div style="margin-left:50px; font-family:tahoma;"><table cellpadding=0 cellspacing=0 border=0>' +
'<tr bgcolor=#4b6c9e>'
+ '<td align=center><font face="calibri" color=White><b>Col1</b></font></td>'
+ '<td align=center><font face="calibri" color=White><b>Col2</b></font></td>'
+ '<td align=center><font face="calibri" color=White><b>Col3</b></font></td>'
+ '<td align=center><font face="calibri" color=White><b>Col4</b></font></td>'
+ '<td align=center><font face="calibri" color=White><b>Col5</b></font></td>'
+
'</tr>'
select #tablehtml --has the correct value at this point
select #body =
(
select ROW_NUMBER() over(order by id) % 2 as TRRow,
td = col1,
td = col2,
td = col3,
td = col4,
td = col5
from #tableUpdate
where notificationType = 'NEWDATE'
order by clname
for XML raw('tr'), elements
)
select #body --empty?!
set #body = REPLACE(#body, '<td>', '<td align=center><font face="tahoma">')
set #body = REPLACE(#body, '</td>', '</font></td>')
set #body = REPLACE(#body, '_x0020_', space(1))
set #body = Replace(#body, '_x003D_', '=')
set #body = Replace(#body, '<tr><TRRow>0</TRRow>', '<tr bgcolor=#F8F8FD>')
set #body = Replace(#body, '<tr><TRRow>1</TRRow>', '<tr bgcolor=#EEEEF4>')
set #body = Replace(#body, '<TRRow>0</TRRow>', '')
select #body --still empty
select #tableHTML = #tableHTML + #body
+ '</table></div></body></html>'
select #tablehtml --now empty!
select #tableHTML = '<div style="color:Black; font-size:11pt; font-family:tahoma; width:100px;">' + #tableHTML + '</div>'
select #tableHTML --still empty
EXEC msdb.dbo.sp_send_dbmail
#profile_name = #databaseMailProfileName,
#body = #tableHTML,
#body_format ='HTML',
#recipients = 'me#me.com',
#subject = 'Subject' ;
If I simply pass select * from #tableupdate or similar to the email, it sends me the data in a not very nice format.
The issue is the query below, returns NULL.
select #body =
(
select ROW_NUMBER() over(order by id) % 2 as TRRow,
td = col1,
td = col2,
td = col3,
td = col4,
td = col5
from #tableUpdate
where notificationType = 'NEWDATE'
order by clname
for XML raw('tr'), elements
)
Focus on this single query, and you will solve your issue. Without sample data of #tableUpdate, we can't solve your problem. However, since #body is NULL, concatenating anything to it will also yield NULL.
For example, select 1 + null, 'this string' + null will return two NULLs.
Thus, all of these set operators will still yield NULL for #body
set #body = REPLACE(#body, '<td>', '<td align=center><font face="tahoma">')
set #body = REPLACE(#body, '</td>', '</font></td>')
set #body = REPLACE(#body, '_x0020_', space(1))
set #body = Replace(#body, '_x003D_', '=')
set #body = Replace(#body, '<tr><TRRow>0</TRRow>', '<tr bgcolor=#F8F8FD>')
set #body = Replace(#body, '<tr><TRRow>1</TRRow>', '<tr bgcolor=#EEEEF4>')
set #body = Replace(#body, '<TRRow>0</TRRow>', '')
select #body --still empty
Then, you set #tableHTML to NULL by concatenating it with #body because #body IS NULL
select #tableHTML = #tableHTML + #body
+ '</table></div></body></html>'
Which leaves #tableHTML and #body with the value of NULL.
A stab at fixing this, since you are building a table, would be to set #body like so:
select #body =
cast((select ROW_NUMBER() over(order by id) % 2 as 'td',
'',
isnull(col1,'') as 'td',
'',
isnull(col2,'') as 'td',
'',
isnull(col3,'') as 'td',
'',
isnull(col4,'') as 'td',
'',
isnull(col5,'') as 'td'
from #tableUpdate
where notificationType = 'NEWDATE'
order by clname
for XML path('tr'), elements) as nvarchar(max))

sp_send_dbmail Incorrect syntax near '<'

I'm having a problem sending a HTML formatted email from SQL server.
With the following section of code I get a "Line 1, incorrect syntax near '<'" error.
SET #tableHTML =
'<H1>Progress Report</H1>' +
'<table border="1">' +
'<tr>' +
'<th>Project Name</th>' +
'<th>Platform</th>' +
'<th>Due By</th>' +
'<th>Current Status</th>' +
'<th>Current State</th>' +
'</tr>' +
CAST (
(
SELECT
td = [Project Name], ' ',
td = Platform, ' ',
td = [Due By], ' ',
td = [Current Status], ' ',
td = [Current State], ' '
FROM [dbo].[table_name]
ORDER BY [Current Status] DESC
FOR XML PATH('tr'), TYPE
) AS NVARCHAR(MAX) ) +
'</table>' ;
I cant seem to pin it to anything in particular? Any Idea's?
Thanks
Update 1:
Ok I've run the code in a debug session and have checked the contents of #tableHTML, the contents looks fine and it gets populated with the expected data from my Table.
Meaning the errors coming in from somewhere else, so I've copied the whole query this time.
DECLARE #tableHTML NVARCHAR(MAX);
SET #tableHTML =
'<h1>Progress Report</h1>' +
'<table border="1">' +
'<tr>' +
'<th>Project Name</th>' +
'<th>Platform</th>' +
'<th>Due By</th>' +
'<th>Current Status</th>' +
'<th>Current State</th>' +
'</tr>' +
CAST
(
(
SELECT
td = [Project Name], '',
td = Platform, '',
td = [Due By], '',
td = [Current Status], '',
td = [Current State], ''
FROM [dbo].[table_name]
ORDER BY [Current Status] DESC
FOR XML PATH('tr'), TYPE
) AS NVARCHAR(MAX) ) +
'</table>';
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'db_mail_account',
#recipients = 'example#example.com',
#subject = 'Daily Project Tracking Report',
#query = #tableHTML,
#body_format = 'HTML';
Thanks again.
It looks like you want #tableHTML to be the body of the email, but you're passing it in as #query, which has to contain valid SQL, hence the error.
Try using #body instead:
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'db_mail_account',
#recipients = 'example#example.com',
#subject = 'Daily Project Tracking Report',
#body = #tableHTML,
#body_format = 'HTML';

How to create multiple HTML tables of data inside 1 email

I am trying to send 2 different tables inside of 1 email. I want one table to establish data concerning data stored in 1 database and the other table for a second database of information. I can produce 1 email simply enough but i cannot understand how to set a second table with a completely different query and data in the same email.
here is what i am currently working with:
USE MY DATABASE
DECLARE
#tableHTML NVARCHAR(MAX)
SET #tableHTML =
N'<H1>Lacrosse Inquiries</H1>' +
N'<table border="1" style="text-align: middle;" width:100% height:20px style="cell" cellspacing="0" cellpadding="5">' +
N'<tr><th>FIRST NAME</th><th>LAST NAME</th><th>Entry Term</th></th><th>Market Segment</th><th>State</th><th>Home Number</th><th>Cell Phone</th>'+
CAST ( ( SELECT td = INQUIRY.F_NAME , ''
, td = INQUIRY.L_NAME , ''
, td = INQUIRY.ENTRY_TERM , ''
, td = INQUIRY.MARKET_SEG , ''
, td = EWADDRESS.STATE , ''
, td = EWADDRESS.HOME_PHONE , ''
, td = EWADDRESS.WORK_PHONE , ''
from EWSTUDENT AS INQUIRY inner join EWMULTI1M as sport on sport.EWSTUID=INQUIRY.EWSTUID left JOIN EWADDRESS ON EWADDRESS.EWSTUID=INQUIRY.EWSTUID LEFT JOIN ESTUDENT ON ESTUDENT.EWSTUID=INQUIRY.EWSTUID
where VCODETYPE = 'EACTIV1M' AND VCODE ='CLAX' AND INQUIRY.GENDER = 'F' AND STUD_TYPE ='P' and (CONVERT(VARCHAR(8), sport.CREATE_DT, 112) = CONVERT(varchar(8), GETDATE(), 112))
order by INQUIRY.L_NAME
FOR XML PATH('tr'), TYPE
) AS NVARCHAR(MAX) ) +
N'</table>' ;
EXEC msdb.dbo.sp_send_dbmail
#profile_name= 'Jordan.Sorensen',
#recipients = 'Jordan.sorensen#svu.edu',
#from_address = 'Jordan.Sorensen#svu.edu',
#body = #tableHTML,
#execute_query_database= 'MY DATABASE',
#subject ='Womens Lacrosse Inquiries',
#body_format= 'HTML'
GO
ANY SUGGESTIONS ON HOW TO CREATE TWO DIFFERENT TABLES WITH TWO COMPLETELY DIFFERENT SETS OF DATA IN ONE EMAIL?
FOR XML PATH('tr'), TYPE
) AS NVARCHAR(MAX) ) +
N'</table>' --<-- This is where you 1st query ends add a couple of line breaks
+ N'<br/><br/>' -- and add you second table just like you have done before
+ N'<table border="1" style="text-align: middle;" width:100% height:20px style="cell" cellspacing="0" cellpadding="5">'
+ N'<tr><th>FIRST NAME</th><th>LAST NAME</th><th>Entry Term</th></th><th>Market Segment</th><th>State</th><th>Home Number</th><th>Cell Phone</th>'
+ CAST ( (/*Your Select just like you have done for 1st table*/) AS NVARCHAR(MAX))
+ N'</table>'
Obviously HTML generated from both sql queries are concatenated and set the value to variable #tableHTML, the rest of the stuff remains the same passing parameters to your procedure and etc etc.