先查三件事:数据库邮件是否启用、msdb是否设为TRUSTWORTHY ON、执行账号是否在DatabaseMailUserRole角色中;再检查环境差异与系统表错误日志。

SQL Server 里 sp_send_dbmail 发不出邮件?先查这三件事
绝大多数“存储过程发不了邮件”的问题,根本不在存储过程本身,而是 sp_send_dbmail 的前置条件没满足。它不报错、不抛异常,只默默静默失败——这是最坑的点。
- 数据库邮件配置是否启用:
sysmail_start_sp必须已执行(不是装完就自动开) -
msdb数据库是否设为信任(TRUSTWORTHY ON)?尤其在使用 EXECUTE AS USER 场景下,否则权限链断裂 - 当前执行账号是否在
DatabaseMailUserRole角色中?注意:不是sysadmin就自动有权限
存储过程中调用 sp_send_dbmail 的安全写法
直接拼接字符串传给 @body 或 @subject 很危险,容易被 SQL 注入,也难调试。更稳妥的方式是用变量 + CONCAT 或 FORMATMESSAGE 构造内容。
- 避免在
@body中硬写单引号嵌套,改用双单引号转义或变量拼接 -
@profile_name必须显式指定,不能依赖默认值;不同环境 profile 名可能不同(如 'ProdAlerts' vs 'DevMail') - 加
@mailitem_id输出参数捕获 ID,后续可查sysmail_mailitems表确认是否入队成功
DECLARE @body NVARCHAR(MAX) = CONCAT('错误发生于:', GETDATE(), ',影响行数:', @affected_rows);
EXEC msdb.dbo.sp_send_dbmail
@profile_name = 'AlertProfile',
@recipients = 'ops@company.com',
@subject = '【PROD】订单处理失败告警',
@body = @body,
@mailitem_id = @mail_id OUTPUT;
存储过程里发邮件失败却没提示?加这个兜底检查
sp_send_dbmail 返回值永远是 0(成功入队),不代表邮件真发出去了。真正失败发生在异步队列处理阶段,所以必须查系统表。
- 执行后立刻查
sysmail_faileditems,看last_mod_date是否在最近 1 分钟内有新增记录 - 结合
sysmail_event_log查具体错误,常见如:The mail could not be sent to the recipients because of the mail server failure - 不要只依赖
@@ERROR或TRY...CATCH,它们捕不到队列层错误
为什么测试能发、上线就静默?注意三个环境差异点
开发机上能跑通,放到生产存储过程里就失效,大概率栽在这几个地方:
- 数据库上下文切换:
EXEC调用时若未显式指定msdb..sp_send_dbmail,可能因 USE db 切换导致解析失败 - 执行账号变化:SSMS 用 Windows 登录,而作业/应用连接用 SQL 登录账号,后者很可能没被加进
DatabaseMailUserRole - 防火墙或 SMTP 策略:生产邮件服务器常限制 IP 白名单,DB 服务器 IP 可能未放行
最易被忽略的是:SQL Server Agent 作业运行时的上下文默认是 msdb,但普通存储过程调用不是。这点不验证,排查会绕半天。

















