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

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')

Related

TSQL Emailing data field containing URL didn't work

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.

Inline SQL Not Updating

My print statement fails each time and so does my exec, anytime I try to run this statement it just tells me command completed successfully. I added the print statement to try to see what was actually being executed, but I am still mind-blown on this. Can someone here please help me?
Shouldn't the Print statement at least show me what I am trying to run? If I print each variable individually before trying to run the update it shows the correct value, so I can only assume it is something way wrong with my update statement?
Declare #fulldata varchar(30), #rsi varchar(50), #employeename varchar(50), #email varchar(50), #rsi2 varchar(50), #email2 varchar(50),
#rsiID varchar(50), #calldate datetime, #calltime datetime, #orderdate datetime, #email3 varchar(50), #uniqueID int, #sql varchar(max)
Set #fullData = 'tvdb'
Set #rsi = 'Alphabet'
Set #employeename = 'Mike Jones'
Set #email = '123abc#gmail.com'
Set #rsi2 = 'Broccoli'
Set #email2 = 'abc123#gmail.com'
Set #rsiID = 'alt16bc'
Set #calldate = '06/15/2015'
Set #calltime = '12:15:00'
Set #orderdate = '06/16/2015'
Set #email3 = 'pineapple1841#gmail.com'
Set #uniqueID = 172855
Set #sql =
'update '+#fulldata+' '
+ 'set rsi = COALESCE('''+#rsi+''',''''), '
+ 'employeename = COALESCE('''+#employeename+''',''''), '
+ 'email = COALESCE('''+#email+''',''''), '
+ 'rsi2 = COALESCE('''+#rsi2+''',''''), '
+ 'email2 = COALESCE('''+#email2+''',''''), '
+ 'rsiID = COALESCE('''+#rsiID+''',''''), '
+ 'calldate = COALESCE('''+CAST(#calldate As Varchar)+''',''''), '
+ 'calltime = COALESCE('''+CAST(#calltime As Varchar)+''',''''), '
+ 'orderdate = COALESCE('''+CAST(#orderdate As Varchar)+''',''''), '
+ 'email3 = COALESCE('''+#email3+''','''') '
+ 'where uniqueID = '+CAST(#uniqueID As Varchar)+' and '+CAST(#uniqueID As Varchar)+' > 0 '
Print #sql
exec (#sql)
EDIT ---
If I try to insert my statements into a table to check it is null. Which leads me to why is #sql not being assigned?
Insert Into #SqlStatement (sql12) VALUES (#sql)
Select * FROM #SqlStatement
Are you sure you have all lines included here? and you can run it without error?
At least your don't have declaration of #sql.
Even you declare the #sql, this line will give you error:
Set uniqueID = 172855
It should be
Set #uniqueID = 172855
Without assigning values to #uniqueID, your whole #sql is be NULL and print will generate NO output.
update tvdb set rsi = COALESCE('Alphabet',''), employeename = COALESCE('Mike Jones',''), email = COALESCE('123abc#gmail.com',''), rsi2 = COALESCE('Broccoli',''), email2 = COALESCE('abc123#gmail.com',''), rsiID = COALESCE('alt16bc',''), calldate = COALESCE('Jun 15 2015 12:00AM',''), calltime = COALESCE('Jan 1 1900 12:15PM',''), orderdate = COALESCE('Jun 16 2015 12:00AM',''), email3 = COALESCE('pineapple1841#gmail.com','') where uniqueID = 172855 and 172855 > 0
Msg 208, Level 16, State 1, Line 1
Invalid object name 'tvdb'.
To debug your code, you can comment out some lines like:
Set #sql =
'update '+#fulldata+' '
+ 'set rsi = COALESCE('''+#rsi+''',''''), '
---+ 'employeename = COALESCE('''+#employeename+''',''''), '
----+ 'email = COALESCE('''+#email+''',''''), '
----+ 'rsi2 = COALESCE('''+#rsi2+''',''''), '
----+ 'email2 = COALESCE('''+#email2+''',''''), '
----+ 'rsiID = COALESCE('''+#rsiID+''',''''), '
----+ 'calldate = COALESCE('''+CAST(#calldate As Varchar)+''',''''), '
----+ 'calltime = COALESCE('''+CAST(#calltime As Varchar)+''',''''), '
----+ 'orderdate = COALESCE('''+CAST(#orderdate As Varchar)+''',''''), '
----+ 'email3 = COALESCE('''+#email3+''','''') '
----+ 'where uniqueID = '+CAST(#uniqueID As Varchar)+' and '+CAST(#uniqueID As Varchar)+' > 0 '
Print #sql
exec (#sql)
and uncomment one line a time until you find the problematic line.
To catch values and make sure you have a non-null #sql, you need use the COALESCE this way:
Set #sql =
'update '+#fulldata+' ' ...
+ 'email = '''+COALESCE(#email,'')+''','

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';

Using Dynamic SQL in a WHILE Loop

I work for an Insurance Company and creating a custom application. When we apply a coverage to a policy it is stored in a table with a Limit, Deductible, and Premium field. However, there are many coverages that do not follow the norm. They use different fields for Limit, Deductible, and Premium and are even stored in different tables. (Don’t ask why and don’t even try to understand the fact that they are STILL doing this so I need to leave logic to allow for additional coverages like this.) There is also no rhyme or reason to what fields they choose so varies greatly.
So, we have decided to create “LookUp” tables that will store all the information we need for a coverage. This avoids numerous joins – we were having issues with speed and performance of the application.
I need to find a way to prepopulate all these fields into a table. I would like the best option performance-wise. This will end up being a Nightly job – so when we receive a new snapshot of data we will run this query and prepopulate/update the values of the table.
I have the following code, I loop through this for each policy, essentially 614,000 time each night...It takes a LONG time to run.
Is there a better way for me to do this?
SELECT #SQL = N'SELECT #Limit=' + ISNULL(Limit, '''''') + N',#Deductible=' + ISNULL(Deductible, '''''') + N',#Premium=' + ISNULL(Premium, '''''') + N' FROM PHI_PIJ.dbo.ASBYCPP' + N' WHERE ' + PolicyNum + N' = ''' + #PolicyKey +'''' + N' AND BYAOTX' + N'='''+Coverage +''''
FROM PolicySummary.dbo.Coverage
WHERE Coverage = #Coverage
AND LOB = 'BOP'
EXEC sp_executesql #SQL, N'#Limit nVarChar(255) output,#Deductible nVarChar(255) output,#Premium nVarChar(255) output', #Limit output, #Deductible output, #Premium output
UPDATE PolicySummary.dbo.CoverageLkup
SET Limit = #Limit, Deductible = #Deductible, Premium = #Premium
WHERE CovAbbrev = #Coverage
AND PolicyKey = #PolicyKey
Here is what is inside my While Loop:
SELECT #SQL = N'SELECT #Limit=' + ISNULL(Limit, '''''') + N',#Deductible=' + ISNULL(Deductible, '''''') + N',#Premium=' + ISNULL(Premium, '''''') + N' FROM PHI_PIJ.dbo.' + [File] + N' WHERE ' + PolicyNum + N' = ''' + #PolNum +'''' + N' AND ' + #CovField + N'='''+Coverage +''''
FROM PolicySummary.dbo.Coverage
WHERE Coverage = #Coverage
AND LOB = #LOB
EXEC sp_executesql #SQL, N'#Limit nVarChar(255) output,#Deductible nVarChar(255) output,#Premium nVarChar(255) output', #Limit output, #Deductible output, #Premium output
INSERT INTO #OptCov2 VALUES(#Coverage, #Description, #LOB, #Limit, #Deductible, #Premium)

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.