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

如何改写SQL Server 2016邮件发送逻辑消除重复SQL语句

优化方案

优先选择表变量/临时表方案,是最适配你场景的解决方式:

  • 公共查询仅需编写、执行1次,后续调整逻辑只需要修改一处,完全消除重复代码
  • 避免相同查询执行两次,数据量大时性能优势明显
  • 实现逻辑简单,不需要额外处理动态SQL的转义、注入风险
    如果公共查询返回的数据量较大(超过1000行),可以把表变量换成临时表#mailData,查询性能更优。

具体实现代码

DECLARE @mail varchar(255)=''
DECLARE @mailBody nvarchar(max)=''
-- 定义表变量存储公共查询结果,仅查询1次
DECLARE @mailData TABLE (
    mailBody nvarchar(max),
    mailFooter varchar(50),
    mail varchar(255)
)
-- 公共查询仅编写一次,写入表变量
INSERT INTO @mailData(mailBody, mailFooter, mail)
select 'mail body content-1' as 'mailBody','11/9/221' as 'mailFooter','a@a.com' as 'mail'
union all
select 'mail body content-2' as 'mailBody','11/09/2021' as 'mailFooter','b@b.com' as 'mail'
union all
select 'mail body content-3' as 'mailBody','10/09/2021' as 'mailFooter','a@a.com' as 'mail'

-- 提前按收件人聚合所有邮件内容,减少循环内查询逻辑
DECLARE @mailAgg TABLE (
    mail varchar(255),
    mailBody nvarchar(max)
)
INSERT INTO @mailAgg(mail, mailBody)
SELECT 
    m.mail,
    -- 增加TYPE和.value处理,避免XML转义特殊字符(<>&等)
    (
        SELECT c.mailBody as 'span', c.mailFooter as 'small' 
        FROM @mailData c 
        WHERE c.mail = m.mail 
        FOR XML PATH ('div'), TYPE
    ).value('.', 'nvarchar(max)') as mailBody
FROM @mailData m
GROUP BY m.mail

-- 游标直接遍历聚合后的结果即可
DECLARE db_cursor CURSOR FOR
SELECT mail, mailBody FROM @mailAgg

OPEN db_cursor
FETCH NEXT FROM db_cursor INTO  @mail, @mailBody

WHILE @@FETCH_STATUS = 0  
BEGIN
    -- 直接调用发送邮件存储过程,无需额外查询
    EXEC msdb.dbo.sp_send_dbmail
        @recipients = @mail,
        @body = @mailBody,
        @body_format = 'HTML'

    FETCH NEXT FROM db_cursor INTO @mail, @mailBody
END

CLOSE db_cursor
DEALLOCATE db_cursor 

通用存储过程封装方案

如果需要支持动态传入不同的查询逻辑,可以结合动态SQL封装为通用存储过程:

CREATE PROCEDURE dbo.usp_SendBatchMail
    @querySQL nvarchar(max) -- 入参为公共查询SQL,固定返回mailBody、mailFooter、mail三个字段
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @mail varchar(255)=''
    DECLARE @mailBody nvarchar(max)=''
    DECLARE @mailData TABLE (
        mailBody nvarchar(max),
        mailFooter varchar(50),
        mail varchar(255)
    )
    -- 执行传入的动态查询,写入表变量
    INSERT INTO @mailData(mailBody, mailFooter, mail)
    EXEC sp_executesql @querySQL

    -- 聚合、发送逻辑和上文一致
    DECLARE @mailAgg TABLE (
        mail varchar(255),
        mailBody nvarchar(max)
    )
    INSERT INTO @mailAgg(mail, mailBody)
    SELECT 
        m.mail,
        (
            SELECT c.mailBody as 'span', c.mailFooter as 'small' 
            FROM @mailData c 
            WHERE c.mail = m.mail 
            FOR XML PATH ('div'), TYPE
        ).value('.', 'nvarchar(max)') as mailBody
    FROM @mailData m
    GROUP BY m.mail

    DECLARE db_cursor CURSOR FOR
    SELECT mail, mailBody FROM @mailAgg

    OPEN db_cursor
    FETCH NEXT FROM db_cursor INTO  @mail, @mailBody

    WHILE @@FETCH_STATUS = 0  
    BEGIN
        EXEC msdb.dbo.sp_send_dbmail
            @recipients = @mail,
            @body = @mailBody,
            @body_format = 'HTML'

        FETCH NEXT FROM db_cursor INTO @mail, @mailBody
    END

    CLOSE db_cursor
    DEALLOCATE db_cursor 
END

调用示例:

DECLARE @sql nvarchar(max) = N'
select ''mail body content-1'' as ''mailBody'',''11/9/221'' as ''mailFooter'',''a@a.com'' as ''mail''
union all
select ''mail body content-2'' as ''mailBody'',''11/09/2021'' as ''mailFooter'',''b@b.com'' as ''mail''
union all
select ''mail body content-3'' as ''mailBody'',''10/09/2021'' as ''mailFooter'',''a@a.com'' as ''mail''
'
EXEC dbo.usp_SendBatchMail @querySQL = @sql
其他方案说明
  • STRING_AGG:SQL Server 2016不支持该函数(2017及以上版本才可用),不适配你的场景
  • 条件循环:实现复杂度高于临时表方案,没有必要采用
  • sp_execute:仅在需要封装通用存储过程、动态传入查询逻辑时使用,普通场景不需要额外引入动态SQL的复杂度
原有实现合理性说明

你原有的按收件人分组、单独发送邮件的逻辑是合理的,符合业务需求,仅存在重复代码的冗余问题,用上述方案优化即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 07:39:02