You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何仅在查询有结果时调用sp_send_dbmail?附存储过程代码

如何让SQL存储过程仅在查询返回非空结果时发送邮件?

你的两个思路——统计结果行数或者用@@ROWCOUNT判断——都是可行的方向,但有个关键细节需要注意:你原来的存储过程里并没有实际执行查询,只是把查询字符串赋值给了变量,所以直接用@@ROWCOUNT是拿不到查询结果行数的,得先自己执行一次查询来判断是否有数据,再决定是否发送邮件。

下面给你具体的实现方案和优劣对比:

方案1:用EXISTS快速判断是否存在结果(推荐)

如果只是要判断有没有符合条件的记录,EXISTS是效率最高的方式——它找到第一条匹配的记录就会停止扫描,不用遍历整个表统计行数,尤其适合数据量大的场景。

修改后的存储过程代码:

ALTER proc dbo.spBornBefore2000 
as 
set nocount on 

DECLARE @sub VARCHAR(100), @msg VARCHAR(250), @query NVARCHAR(max), @tab char(1) = CHAR(9)

-- 检查是否存在符合条件的记录,找到第一条就停止
IF EXISTS(SELECT 1 FROM people WHERE year(dob) < 2000)
BEGIN
    SELECT @sub = 'before 1990 -' + cast(getdate() as varchar(100))
    SELECT @msg = N'blablabla.....'
    SELECT @query = ' select id, name, dob, year(dob) as birth_year from people where year(dob) < 2000 '

    EXEC msdb.dbo.sp_send_dbmail 
        @profile_name = 'USERS', 
        @recipients = 'abc@gmail.com', 
        @body = @msg, 
        @subject = @sub, 
        @query = @query, 
        @query_attachment_filename = 'before2000.csv', 
        @attach_query_result_as_file = 1, 
        @query_result_header = 1, 
        @query_result_width = 256 , 
        @query_result_separator = @tab, 
        @query_result_no_padding =1;
END

方案2:统计结果行数

如果你需要知道具体有多少条记录(比如要把数量写到邮件正文里),可以先统计行数再判断:

ALTER proc dbo.spBornBefore2000 
as 
set nocount on 

DECLARE @sub VARCHAR(100), @msg VARCHAR(250), @query NVARCHAR(max), @tab char(1) = CHAR(9)
DECLARE @recordCount INT

-- 统计符合条件的记录数
SELECT @recordCount = COUNT(*) FROM people WHERE year(dob) < 2000

IF @recordCount > 0
BEGIN
    -- 可以把记录数加到邮件正文里
    SELECT @msg = N'共找到 ' + CAST(@recordCount AS VARCHAR(10)) + ' 条1999年及以前出生的用户,详情见附件。'
    SELECT @sub = 'before 1990 -' + cast(getdate() as varchar(100))
    SELECT @query = ' select id, name, dob, year(dob) as birth_year from people where year(dob) < 2000 '

    EXEC msdb.dbo.sp_send_dbmail 
        @profile_name = 'USERS', 
        @recipients = 'abc@gmail.com', 
        @body = @msg, 
        @subject = @sub, 
        @query = @query, 
        @query_attachment_filename = 'before2000.csv', 
        @attach_query_result_as_file = 1, 
        @query_result_header = 1, 
        @query_result_width = 256 , 
        @query_result_separator = @tab, 
        @query_result_no_padding =1;
END

方案3:用@@ROWCOUNT判断

这种方式需要你先执行一次查询,然后立刻获取@@ROWCOUNT的值(中间不能插其他SQL语句,否则@@ROWCOUNT会被覆盖):

ALTER proc dbo.spBornBefore2000 
as 
set nocount on 

DECLARE @sub VARCHAR(100), @msg VARCHAR(250), @query NVARCHAR(max), @tab char(1) = CHAR(9)

-- 执行查询(因为set nocount on,不会把结果返回给客户端)
SELECT id, name, dob, year(dob) as birth_year 
FROM people 
WHERE year(dob) < 2000

-- 立刻获取行数
IF @@ROWCOUNT > 0
BEGIN
    SELECT @sub = 'before 1990 -' + cast(getdate() as varchar(100))
    SELECT @msg = N'blablabla.....'
    SELECT @query = ' select id, name, dob, year(dob) as birth_year from people where year(dob) < 2000 '

    EXEC msdb.dbo.sp_send_dbmail 
        @profile_name = 'USERS', 
        @recipients = 'abc@gmail.com', 
        @body = @msg, 
        @subject = @sub, 
        @query = @query, 
        @query_attachment_filename = 'before2000.csv', 
        @attach_query_result_as_file = 1, 
        @query_result_header = 1, 
        @query_result_width = 256 , 
        @query_result_separator = @tab, 
        @query_result_no_padding =1;
END

各方案对比

  • EXISTS:效率最高,逻辑简洁,适合只需要判断“有没有”的场景。
  • 统计行数:适合需要知道具体记录数的场景,但数据量大时会比EXISTS慢一点。
  • @@ROWCOUNT:需要严格保证执行查询后立刻获取值,容易因为中间加了其他代码导致判断错误,一般不推荐作为首选。

最后提醒:不管用哪种方案,sp_send_dbmail里的查询还是会再执行一次(因为它是在独立的会话中运行的),如果你的查询非常耗时,可以考虑把结果先存入全局临时表或者永久表,但对于一般业务场景,两次查询的开销可以忽略不计。

内容的提问来源于stack exchange,提问作者not_ur_avg_cookie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:03:32