背景 (Background)

In my organization, the developers send a lot of database generated emails for informational purposes to ensure that everything in the system is still working 100%.We can call them production messages. Normally the way the email is presented is not important as it is only meant for the developer to help with system health check, but every now and again a business user would ask for the report (which was never meant to be an actual report) and then we would include the business user in the distribution list for this email.

在我的组织中,开发人员发送了大量数据库生成的电子邮件,以供参考,以确保系统中的所有内容仍在100%正常工作。我们可以称其为生产消息。 通常,电子邮件的呈现方式并不重要,因为它仅意味着开发人员可以帮助进行系统运行状况检查,但是企业用户会不时地要求提供报告(这从来都不是真正的报告),并且那么我们会将业务用户包含在此电子邮件的分发列表中。

讨论区 (Discussion)

After we have included the business user in the mail group, it does not take very long for them to start complaining about how it is being presented and due to the huge backlog in our BI department, there is no real time to create a proper report in our reporting tools.

在将业务用户包括在邮件组中之后,他们很快就可以开始抱怨它的呈现方式,并且由于我们BI部门的大量积压,没有实时的时间来创建适当的报告在我们的报告工具中

I started to think about the way we normally send these emails, you write your query and cast it to XML and pass the XML on the sp_send_dbmail procedure and define the body format as HTML. This was the key as most of us have worked with HTML sometime in our lives and I am not even close to being able to call myself an HTML expert, but I do know that you can define inline styling for HTML. As soon as this thought crossed my mind I went online to search for an HTML/CSS generator as I cannot remember how to generate CSS scripts. I found this great free HTML table generator website Tables Generator , this was exactly what I needed to format my tables in my emails. So I created a table on the generator to match our company branding and started to incorporate this into my emails. If I must say so myself I was really impressed with the end result and the business users stopped complaining about the ugly reports that they were not supposed to get in the first place.

我开始考虑我们通常发送这些电子邮件的方式,您编写查询并将其转换为XML,然后在sp_send_dbmail过程中传递XML,并将主体格式定义为HTML。 这是关键,因为我们大多数人一生中都有使用HTML的经验,我什至不能称自己为HTML专家,但是我确实知道您可以为HTML定义内联样式。 这个想法一经我想到,我就上网搜索HTML / CSS生成器,因为我不记得如何生成CSS脚本。 我找到了这个很棒的免费HTML表生成器网站Tables Generator ,这正是我在电子邮件中格式化表所需要的。 因此,我在生成器上创建了一个表格以匹配我们公司的品牌,并开始将其合并到我的电子邮件中。 如果我必须对自己说的话,那么最终的结果给我留下了非常深刻的印象,业务用户也不再抱怨丑陋的报道,说他们不应该成为第一人。

I had to find a solution that would not take ages to create but looks good enough for the business to look at, and by using features/tools that are already at my exposure.

我必须找到一种解决方案,该解决方案需要花很长时间才能创建,但是要看起来足够好以使企业可以使用它,并且要使用已经公开展示的功能/工具。

注意事项 (Considerations)

This solution is in no way a replacement for proper reporting and BI, as it is very basic and static reports and I would not recommend using DBMail to extensively as it can possibly create extra overhead in your production environment. In saying this DBMail, uses service broker and should, in theory, have almost zero impact on your production environment but rather be safe than sorry.

此解决方案绝对不能替代适当的报告和BI,因为它是非常基本和静态的报告,我不建议广泛使用DBMail,因为它可能在生产环境中产生额外的开销。 在说这个DBMail时,它使用服务代理,并且从理论上讲应该对您的生产环境产生几乎零的影响,但是安全而不是遗憾。

先决条件 (Prerequisites)

  • DB Mail needs to be setup, books online have great articles on how to achieve this.

    需要设置DB Mail,在线书籍中有很多有关如何实现此目的的文章。

  • AdventureWorks database is needed if you are going to follow the examples in this article.

    如果要遵循本文中的示例,则需要AdventureWorks数据库。

目的 (Objective)

We will be creating our result set in AdventureWorks and then create the script that will use to generate the CSS/HTML email. The end result should look something like the below screenshot of the table we are going to generate and include in our email.

我们将在AdventureWorks中创建结果集,然后创建将用于生成CSS / HTML电子邮件的脚本。 最终结果应类似于我们将要生成并包含在电子邮件中的表格的以下屏幕截图。

解 (Solution)

First things first let’s create the SQL query that we will use to generate our required result set. In this case, we want to send a summary of the current week’s orders to ourselves and maybe even someone from the business. This could give a developer an indication of the systems performance and business a piece of mind.

首先,让我们创建一个SQL查询,该查询将用于生成所需的结果集。 在这种情况下,我们希望将本周订单的摘要发送给自己,甚至可能发送给业务部门的人。 这可以使开发人员轻松了解系统性能和业务。


DECLARE @ReportingPeriodStart DATE;
DECLARE @ReportingPeriodEnd DATE;-- Reporting date will from the beginning of the current week
SET @ReportingPeriodStart = DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(YEAR, -3, GETDATE())), 0);
SET @ReportingPeriodEnd = DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(YEAR, -3, GETDATE())) + 1, 0);PRINT @ReportingPeriodStart
PRINT @ReportingPeriodEndSELECT  CONVERT(CHAR(8), OrderDate, 112) ,SM.Name ,COUNT(SalesOrderID) ,CAST(SUM(TotalDue) AS NUMERIC(18, 2))
FROM    [Sales].[SalesOrderHeader] SOHJOIN Purchasing.ShipMethod SM ON SM.ShipMethodID = SOH.ShipMethodID
WHERE   SOH.OrderDate >= @ReportingPeriodStartAND SOH.OrderDate < @ReportingPeriodEnd
GROUP BY CONVERT(CHAR(8), OrderDate, 112) ,SM.Name
ORDER BY 1 ,2;

I use two variables to get the start of the week and then end of the current week. We have to set the current date back 3 years to be able to get any results back from AdventureWorks. There is no need to use variables in the script you can place your date functions inside of the WHERE clause, but we could use this variable later on again for the subject of my email and maybe even to add a header to the body of the email. There is no need to alias the columns in the script as the HTML headers will handle this for us and when you convert the query to HTML you have to alias every column with ‘td’ and insert a blank column after every column defined in the query above.

我使用两个变量来获取本周的开始时间,然后是本周的结束时间。 我们必须将当前日期设置为3年,以便能够从AdventureWorks获得任何结果。 您无需在脚本中使用变量,您可以将日期函数放在WHERE子句中,但是稍后我们可以再次将此变量用于我的电子邮件主题,甚至可以在电子邮件正文中添加标头。 无需在脚本中对列进行别名处理,因为HTML标头将为我们处理此问题,当您将查询转换为HTML时,您必须对每个列都使用“ td”作为别名,并在查询中定义的每个列之后插入空白列以上。

Your next step would be to go and generate the HTML and CSS that you are going to use, or you could just use mine that I have already generated. I have generated 2 styles to be able to include 2 header rows in our email.

下一步将是生成将要使用HTML和CSS,或者只使用我已经生成的我HTML和CSS。 我生成了2种样式,能够在我们的电子邮件中包含2个标题行。


/*
Declare Variables for HTML
*/DECLARE @Style NVARCHAR(MAX)= '';/*
Define CSS for html to use
*/
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>';

Once we are happy with the inline style sheet, we can start with generating the HTML that will later be used in the sp_Send_DBMail.

一旦对内联样式表感到满意,我们就可以开始生成HTML,该HTML将在以后的sp_Send_DBMail中使用。


USE AdventureWorks2014;
GO/*
Declare Variables for DML
*/DECLARE @ReportingPeriodStart DATE;
DECLARE @ReportingPeriodEnd DATE;-- Reporting date will from the beginning of the current week
SET @ReportingPeriodStart = DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(YEAR, -3, GETDATE())), 0);
SET @ReportingPeriodEnd = DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(YEAR, -3, GETDATE())) + 1, 0);/*
Declare Variables for HTML
*/DECLARE @tableHTML NVARCHAR(MAX)= '';SET @tableHTML += @Style + @tableHTML + N'<H2>Order Summary For : ' + CAST(@ReportingPeriodStart AS CHAR(10)) + ' to ' + CAST(@ReportingPeriodEnd AS CHAR(10)) + '</H2>' + N'<table class="tg">' --DEFINE TABLE
/*
Define Column Headers and Column Span for each Header Column
*/+ N'<tr>' + N'<th class="tg-hgcj" colspan="2">Order Information</th>' + N'<th class="tg-hgcj" colspan="2">Summary</th>'+ N'</tr>'
/*
Define Column Sub-Headers
*/+ N'<tr>'+ N'<td class="tg-9ajh">Order Date</td>'+ N'<td class="tg-9ajh">Shipping Method</td>' + N'<td class="tg-9ajh">Order QTY</td>'+ N'<td class="tg-9ajh">Order Total</td></tr>'
/*
Define data for table and cast to xml
*/+ CAST(( SELECT td = ISNULL(CONVERT(CHAR(8), OrderDate, 112),'2020-01-01') ,'',td = ISNULL(SM.Name,'UNKNOWN') ,'',td = ISNULL(COUNT(SalesOrderID),0) ,'',td = ISNULL(CAST(SUM(TotalDue) AS NUMERIC(18,2)),0) ,''FROM   [Sales].[SalesOrderHeader] SOHJOIN Purchasing.ShipMethod SM ON SM.ShipMethodID = SOH.ShipMethodIDWHERE   SOH.OrderDate >= @ReportingPeriodStartAND  SOH.OrderDate < @ReportingPeriodEndGROUP BY CONVERT(CHAR(8), OrderDate, 112) ,SM.NameORDER BY 1 ,2FORXML PATH('tr') ,TYPE) AS NVARCHAR(MAX)) + N'</table>';

In the above script, we declare the variable where we are going to insert the HTML string for now. The HTML string starts off with an &LT;H2&GT; tag which allows us to give the table a header, this is very useful for when you want to add more than 1 table to the email.next step is to initiate the <table> tag and create the first header row and define how many columns this header row will span, in other words, we are using the first header row to group the second header rows into logical groupings. Once this is defined we can start to define the second header row. It is very important to remember that the columns you define in the headers match a number of columns that are going to be returned in your result set, excluding the blank columns. We will then have to add our query to the HTML string and cast it to XML with type PATH(‘tr’), this is to tell the HTML that each row in the result set is a table row in HTML. It is also a good idea to handle NULLS as this will shift your HTML table.

在上面的脚本中,我们现在声明要在其中插入HTML字符串的变量。 HTML字符串以&LT; H2&GT;开头。 标记,它使我们可以为表格指定标题,这对于在电子邮件中添加多个表格时非常有用。下一步是启动<table>标记并创建第一个标题行并定义多少列该标题行将跨越,换句话说,我们正在使用第一标题行将第二标题行分组为逻辑分组。 一旦定义了这个,我们就可以开始定义第二个标题行。 重要的是要记住,您在标题中定义的列与要在结果集中返回的许多列匹配,但不包括空白列。 然后,我们必须将查询添加到HTML字符串中,并将其转换为类型为PATH('tr')的XML,这是告诉HTML结果集中的每一行都是HTML中的表格行。 处理NULL也是一个好主意,因为这将移动HTML表。

To test what we have created so far we can print the HTML string.

为了测试到目前为止我们已经创建的内容,我们可以打印HTML字符串。

 PRINT @tableHTML

If everything goes according to plan SSMS should print the entire HTML string to the messages tab. We can copy this string and paste it into a new notepad and save this as .html file format, once this is done we can simply double-click the .html file and preview our results in a browser. I prefer using Notepad++ as my text editor it has an option to save a file as .html. I know you can do this in windows notepad as well if you select all file types and then just manually enter the file type as .html.

如果一切按计划进行,SSMS应该将整个HTML字符串打印到“消息”选项卡。 我们可以复制该字符串并将其粘贴到新的记事本中,然后另存为.html文件格式。完成后,我们只需双击.html文件并在浏览器中预览结果即可。 我更喜欢使用Notepad ++作为我的文本编辑器,它可以选择将文件另存为.html。 我知道您也可以在Windows记事本中执行此操作,如果您选择了所有文件类型,然后手动将文件类型输入为.html。

After you have saved the file you can go to the location of the file and double-click the .html file you just created, and you should see the following table in your browser.

保存文件后,您可以转到文件的位置,然后双击刚刚创建的.html文件,然后您应该在浏览器中看到下表。

Once we are happy with the output, we can move on to sending our first test mail using DBMail.

对输出感到满意之后,我们可以继续使用DBMail发送第一封测试邮件。


--DECLARE @fullFileName varchar(100) = 'E:\Reports\Orders\Archive\OrderDetail.xls'            EXEC msdb.dbo.sp_send_dbmail @profile_name = 'myprofile', @recipients = '*****@outlook.com', @from_address = '*****@outlook.com', @body = @tableHTML, @body_format = 'HTML', @subject = 'My Awesome Report'--, @file_attachments = @fullFileName; --YOU CAN ALSO ATTACH A FILE TO THE MAIL IF NEED BE.

I have included but commented out the code for if you want to attach a file to an email. You will notice that we assign our HTML string variable to the @body parameter of sp_send_dbmail and set the @body_format parameter to ‘HTML’. And this is it to send beautiful emails from DBMail. Below is the code for the entire solution from start to finish.

如果您要将文件附加到电子邮件中,我已经包含但注释掉了代码。 您会注意到,我们将HTML字符串变量分配给sp_send_dbmail的@body参数,并将@body_format参数设置为'HTML'。 这就是从DBMail发送漂亮的电子邮件的过程。 下面是从头到尾的整个解决方案的代码。


USE AdventureWorks2014;
GO/*
Declare Variables for DML
*/DECLARE @ReportingPeriodStart DATE;
DECLARE @ReportingPeriodEnd DATE;-- Reporting date will from the beginning of the current week
SET @ReportingPeriodStart = DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(YEAR, -3, GETDATE())), 0);
SET @ReportingPeriodEnd = DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(YEAR, -3, GETDATE())) + 1, 0);/*
Declare Variables for HTML
*/DECLARE @Style NVARCHAR(MAX)= '';
DECLARE @tableHTML NVARCHAR(MAX)= '';/*
Define CSS for html to use
*/
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 @tableHTML += @Style + @tableHTML + N'<H2>Order Summary For : ' + CAST(@ReportingPeriodStart AS CHAR(10)) + ' to ' + CAST(@ReportingPeriodEnd AS CHAR(10)) + '</H2>' + N'<table class="tg">' --DEFINE TABLE
/*
Define Column Headers and Column Span for each Header Column
*/+ N'<tr>' + N'<th class="tg-hgcj" colspan="2">Order Information</th>' + N'<th class="tg-hgcj" colspan="2">Summary</th>'+ N'</tr>'
/*
Define Column Sub-Headers
*/+ N'<tr>'+ N'<td class="tg-9ajh">Order Date</td>'+ N'<td class="tg-9ajh">Shipping Method</td>' + N'<td class="tg-9ajh">Order QTY</td>'+ N'<td class="tg-9ajh">Order Total</td></tr>'
/*
Define data for table and cast to xml
*/+ CAST(( SELECT td = ISNULL(CONVERT(CHAR(8), OrderDate, 112),'2020-01-01') ,'',td = ISNULL(SM.Name,'UNKNOWN') ,'',td = ISNULL(COUNT(SalesOrderID),0) ,'',td = ISNULL(CAST(SUM(TotalDue) AS NUMERIC(18,2)),0) ,''FROM   [Sales].[SalesOrderHeader] SOHJOIN Purchasing.ShipMethod SM ON SM.ShipMethodID = SOH.ShipMethodIDWHERE   SOH.OrderDate >= @ReportingPeriodStartAND  SOH.OrderDate < @ReportingPeriodEndGROUP BY CONVERT(CHAR(8), OrderDate, 112) ,SM.NameORDER BY 1 ,2FORXML PATH('tr') ,TYPE) AS NVARCHAR(MAX)) + N'</table>';        --DECLARE @fullFileName varchar(100) = 'E:\Reports\Orders\Archive\OrderDetail.xls'            EXEC msdb.dbo.sp_send_dbmail @profile_name = 'myprofile', @recipients = '*****@outlook.com', @from_address = '*****@outlook.com', @body = @tableHTML, @body_format = 'HTML', @subject = 'My Awesome Report'--, @file_attachments = @fullFileName; --YOU CAN ALSO ATTACH A FILE TO THE MAIL IF NEED BE.

You will have to change the parameters of the sp_send_dbmail to match your environment’s settings. Once you have configured this you should receive an email that looks like this.

您将必须更改sp_send_dbmail的参数以匹配您环境的设置。 配置完成后,您将收到一封如下所示的电子邮件。

最后的想法 (Final Thoughts)

I like to create a stored procedure in a special database for every report mail I create, this allows for on central place to maintain all reports. Another way to manage the distribution lists for these emails is to create a lookup table for these report emails. By creating a lookup you will not have to change the stored procedure every time someone wants to be added or removed from this email, you can simply manage this lookup table’s data. I also know it is possible to use the google chart API to add charts to the email, I have also seen someone in my organization that creates the summary as per above and then create a additional .html file that is more interactive and attach it to the email for the users to play a bit with the summary data.

我喜欢在一个特殊的数据库中为我创建的每个报告邮件创建一个存储过程,这使得在中央可以维护所有报告。 管理这些电子邮件的通讯组列表的另一种方法是为这些报告电子邮件创建查找表。 通过创建查找,您不必每次有人想要从此电子邮件中添加或删除该存储过程时,都可以简单地管理该查找表的数据。 我也知道可以使用google chart API将图表添加到电子邮件中,我还看到我组织中的某人按照上述方法创建了摘要,然后创建了一个更具交互性的附加.html文件并将其附加到电子邮件,供用户使用摘要数据。

参考资料 (References)

  • Send DB Mail 发送数据库邮件
  • HTML Table Generator HTML表格生成器
  • Example of HTML in DB Mail DB Mail中HTML示例

翻译自: https://www.sqlshack.com/format-dbmail-with-html-and-css/

使用HTML和CSS格式化DBMail相关推荐

  1. vscode中使用prettier后html,css格式化不生效的问题

    vscode里装上prettier插件后格式化文件后不生效. 例如这里我之前装上插件后当时只设置了HTML默认格式化程序,以为后面格式化其他文件就不用在设置了,然后今天写css的时候突然发现prett ...

  2. 《使用CSS格式化布局——页面布局》

    /* (程序头部注释开始) * 程序的版权和版本声明部分 * Copyright (c) 2011, 烟台大学计算机学院学生 * All rights reserved. * 文件名称:   < ...

  3. CSS 格式化上下文详解

    CSS格式化上下文 CSS 中很重要的一环就是 BFC 块级格式化上下文,本文整理了 CSS BFC 的相关知识,建议沐浴更衣后食用. 在正式讲解BFC之前,先了解一下浏览器外边距重叠的问题. 外边距 ...

  4. php怎么调用css格式化,css文件格式化脚本的方法

    这次给大家带来css文件格式化脚本的方法,css文件格式化脚本的注意事项有哪些,下面就是实战案例,一起来看一下.#!/usr/bin/python # -*- coding: UTF-8 -*- im ...

  5. CSS格式化、兼容及常用样式(switch开关记得收藏)

    目录 内容介绍 一.格式化 1.select格式化样式 2.button格式化样式 3.input去除边框 4.selection去除下拉三角按钮 5.textarea隐藏右下角拖动及滚动条 二.兼容 ...

  6. CSS格式化文字排版

    对文字排版都有哪些操作?常用word的人肯定能够随口道来:字体.字号.颜色.缩进.对齐.行高.下划线等等.没错,这些都是在文字排版中比较常用的项.同样也用于网页页面的文字排版中. 字体 为网页中的文字 ...

  7. word文档css格式化,css教程 word版.doc

    css教程 word版 CSS教程 CSS简介 CSS语法 CSS字体属性(font) CSS常用文本属性 CSS背景属性 CSS边框属性(border) CSS边距属性(margin) CSS间隙属 ...

  8. 解析 CSS 格式化上下文

    ⭐️ 更多前端技术和知识点,搜索订阅号 JS 菌 订阅 ✴️ BFC 块级格式化上下文 BFC(Block Formatting Contexts),块级格式化上下文.BFC 实际上就是页面中一块渲染 ...

  9. 简书的css排版,css格式化排版

    1,文字排版 1-1,字体 .p1 { font-family: "宋体" } 运行效果: 第一个段落为"宋体" 1-2,字号和颜色 p { font-size ...

最新文章

  1. myEclipse开发内存溢出解决办法myEclipse调整jvm内存大小java.lang.OutOfMemoryError: PermGen space及其解决方法...
  2. 带线的无限级下拉树列表-完整示例篇
  3. MySQL数据库概述
  4. java实现最小二乘法
  5. Xshell批量导入IP地址
  6. FFmpeg(六) 播放视频之GLSurfaceView显示RGB数据
  7. DirectX9:总结篇 异常错误检测
  8. Java并发--ConcurrentModificationException(并发修改异常)异常原因和解决方法
  9. Code Project精彩系列(1)
  10. 在linux 命令行下从http下载东西
  11. 字符串压缩算法(腾讯笔试题)
  12. Python之网络编程
  13. 量子计算机的成熟度模型,全球首家:紫光展锐通过 TMMi 软件测试成熟度模型集成 5 级认证...
  14. Python之人民币与美元的换算
  15. 【时空序列预测实战】风险时空预测?keras之ConvLSTM实战来搞定
  16. 麦克斯韦方程的积分形式及应用、麦克斯韦方程组的微分形式及应用
  17. 配置mysql开启定时任务_mysql设置定时任务
  18. Android中的AlarmManager的使用
  19. CMM与CMMI的关系已经软件全面质量管理的思想体系
  20. 三坐标检测基础知识之测针选型技巧

热门文章

  1. Windows Azure Azure 简介
  2. P1601高精度加法
  3. spark页面单跳转化率
  4. 用原生javascript做的一个打地鼠的小游戏
  5. MySQL---分组查询
  6. jQuery学习(一)—jQuery应用步骤以及ready事件和load事件的区别
  7. OSS开源软件是什么
  8. 2021年贵金属黄金会迎来大行情吗?
  9. 都说比特币无价值,涨得不合理;但你知道比特币最大的用途吗?
  10. 在创业之路上,每个人都会有很多的老师