sp_send_dbmail需先启用数据库邮件功能并启动sql server agent服务,配置有效邮件配置文件、授权用户角色,并确保参数完整正确;调用成功仅表示入队,须查sysmail_log等视图确认实际发送结果。

sp_send_dbmail 要求数据库邮件功能已启用
直接调用 sp_send_dbmail 报错“无法找到存储过程”,大概率是因为数据库邮件没配置或服务未启动。它不是 SQL Server 自带即用的功能,必须手动启用代理、配置账户和配置文件。
检查是否启用:SELECT is_broker_enabled FROM sys.databases WHERE name = 'msdb' —— 必须为 1;EXEC msdb.dbo.sysmail_help_status_sp —— 返回 STARTED 才表示服务运行中。
常见漏掉的步骤:
- SQL Server Agent 服务未启动(部分环境要求 Agent 启动才能触发邮件队列)
- 执行用户没有
DatabaseMailUserRole角色(在msdb中添加) - 配置文件未设为默认,或未授权给当前用户(用
sysmail_add_principalprofile_sp绑定)
在存储过程中调用 sp_send_dbmail 的基本写法
不能只写 EXEC msdb.dbo.sp_send_dbmail 就完事——参数缺失或类型错会导致静默失败(邮件不发,也不报错),尤其 @profile_name 和 @recipients 是必填项。
最小可行调用示例:
EXEC msdb.dbo.sp_send_dbmail @profile_name = 'MyProfile', @recipients = 'admin@company.com', @subject = '订单处理完成', @body = '今日共处理订单 42 笔。';
关键注意点:
-
@profile_name必须是sysmail_profile表里真实存在的名称(区分大小写,建议用双引号或方括号包裹) -
@recipients只接受逗号分隔的邮箱字符串,不能传变量拼接后含空格或换行(先REPLACE(@email_list, CHAR(10), '')清理) -
@body如果内容来自查询结果,用SELECT ... FOR XML PATH('')或STRING_AGG(SQL Server 2017+)组装,避免隐式转换截断
带查询结果的邮件体:用 @query + @execute_query_database 安全拼接
想把某张表的最新 10 条记录发成表格邮件?别用 CONCAT 拼 HTML 字符串——易 XSS、难对齐、性能差。正确做法是让 sp_send_dbmail 自动执行查询并格式化为 HTML 表格。
示例:
EXEC msdb.dbo.sp_send_dbmail @profile_name = 'MyProfile', @recipients = 'dev@company.com', @subject = '异常日志摘要', @query = 'SELECT TOP 5 ErrorTime, ErrorMessage FROM dbo.ErrorLog WHERE ErrorTime > DATEADD(hh, -24, GETDATE()) ORDER BY ErrorTime DESC', @execute_query_database = 'YourDBName', @query_result_header = 1, @query_result_width = 512, @query_result_separator = ' ', @attach_query_result_as_file = 0, @query_result_no_padding = 1;
容易出问题的地方:
-
@execute_query_database必须显式指定库名,否则默认在msdb执行,查不到你的业务表 -
@query里的 SQL 不能含 GO、USE、临时表(#开头),也不能引用当前会话变量(如@myvar) - 如果查询返回大量数据,
@query_result_width太小会自动截断字段,建议设为 1024 或更高 - 想发 HTML 表格但又不想要附件?确保
@attach_query_result_as_file = 0,且不要设@body_format = 'HTML'——它不控制查询结果格式,只影响@body参数本身
错误捕获与调试:邮件发不出时看 sysmail_log 和 sysmail_event_log
调用成功返回 0 不代表邮件真发出去了——只是进队列了。真正失败发生在后台发送线程,必须查系统视图。
快速定位问题:
-
SELECT * FROM msdb.dbo.sysmail_log WHERE event_type = 'error' ORDER BY log_date DESC—— 看具体错误描述,比如“SMTP server rejected”、“Authentication failed” -
SELECT * FROM msdb.dbo.sysmail_faileditems—— 查哪些邮件卡住、失败原因、重试次数 -
SELECT * FROM msdb.dbo.sysmail_event_log ORDER BY last_mod_date DESC—— 查 SMTP 连接、认证、超时等底层事件
特别提醒:sp_send_dbmail 在存储过程中执行时,若事务未提交就发邮件,邮件内容可能读到未提交数据(脏读)。如需强一致性,要么把邮件逻辑放到事务外,要么在 @query 中加 WITH (NOLOCK) 明确语义,别依赖默认隔离级别。










