必须用FOR XML PATH('')+TYPE构造HTML表格并手动编码字段值,因@body手拼易截断乱码、特殊字符未转义;@query参数无法访问存储过程变量,需先查入表变量再转HTML;发送仅入队,须查sysmail_allitems确认真实状态。

能发,但直接拼 @body 字符串大概率出错:截断、乱码、HTML 标签被解析、特殊字符未转义——必须用 FOR XML PATH('') + TYPE 构造表格,再手动 HTML 编码字段值。
sp_send_dbmail 的 @query 参数不能直接用于动态条件查询
很多人想在存储过程中写 @query = 'SELECT * FROM t WHERE status = ' + @status,然后传给 sp_send_dbmail。这会失败,因为:
-
@query是在邮件发送时才执行的独立上下文,此时你的存储过程早已退出,@status变量、临时表、表变量全不可见 - SQL Server 会把整个字符串当字面量执行,不支持变量插值,除非你用动态 SQL 拼出完整语句再
EXEC,但那不是@query的用法 - 更稳妥的做法是:先查数据进表变量或临时表,再用
FOR XML把它转成 HTML 表格字符串,最后塞进@body
HTML 表格生成必须防截断和乱码
NVARCHAR(MAX) 看似够大,但手拼 HTML 时一不小心就触发隐式截断(尤其字段含长文本或 NULL);另外中文、&、 不转义会导致邮件内容丢失或格式崩溃:
- 用
FOR XML PATH('') + TYPE替代+拼接,它天然支持NVARCHAR(MAX)容量且不会意外截断 - 对每个字段值做三重替换:
REPLACE(REPLACE(REPLACE(@val, '&', '&'), '', '>'),顺序不能错(先 & 后 ) - 避免在
@body中直接嵌入用户输入字段,比如地址、备注这类长文本,优先只放 ID,收件人点链接跳转查看详情 - 开头加
<meta charset="utf-8">声明编码,防止 Outlook 解析成 GB2312 导致中文乱码
邮件是否真发出去了?别信 @@ERROR 和返回值 0
sp_send_dbmail 执行完永远返回 0,只表示“成功入队”,不代表邮件已送达:
立即学习“前端免费学习笔记(深入)”;
-
@@ERROR永远为 0,完全没用 - 查真实状态得看系统表:
SELECT * FROM msdb.dbo.sysmail_allitems WHERE subject LIKE '%你的主题%',重点看sent_status字段(succeeded/failed/retrying) - 失败原因藏在
sysmail_event_log里,比如 SMTP 认证失败、附件路径不存在、HTML 标签不闭合等 - 如果用的是 Windows 身份验证(
-T参数)调bcp导出 Excel 再作附件,注意 SQL Server Agent 服务账户是否有对应磁盘写权限
真正难的不是写出来,而是让 HTML 表格在 Outlook、iOS 邮件、Gmail 里都显示正常——内联样式比 CSS 类靠谱,<table border="1"> 比 style="border:1px" 兼容性好,还有别忘了测试空结果集时表格是否仍能渲染(加个 IF @@ROWCOUNT = 0 分支兜底)。这些细节不验证,发一百封也收不到反馈。



















