如何仅在查询有结果时调用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
相关产品推荐
相关产品推荐

