【问题标题】:How to "iterate" through a SQL result in stored procedure but avoid using a cursor?如何“迭代”存储过程中的 SQL 结果但避免使用游标?
【发布时间】:2014-02-04 16:25:39
【问题描述】:

我对做更多涉及的 SQL 工作还很陌生。

我的目标是使用查询结果发送电子邮件以推动电子邮件的创建。我打算在 SQL Server 2008 上创建一个存储过程并每周安排两次(最多有 20 封电子邮件,这不会是沉重的电子邮件负载)。

SELECT ProjectCodes.ProjectCode, COUNT(Projects.ProjectsID),  ProjectApprovers.EmailApprover
FROM Projects 
INNER JOIN ProjectCodes
    ON Projects.ProjectCodesID=ProjectCodes.ProjectCodesID
INNER JOIN ProjectApprovers
    ON Projects.ProjectCodesID=ProjectApprovers.ProjectCodesID
WHERE ProjectApprovers.IsPrimaryApprover=1
group by ProjectCodes.ProjectCode, ProjectApprovers.EmailApprover

这会返回类似于:

+-------------+-------+--------------+
| ProjectCode | Count | EmailAddress |
+-------------+-------+--------------+
| Code1       |     4 | Email1       |
| Code2       |     2 | Email2       |
| Code3       |     2 | Email3       |
| Code4       |     3 | Email4       |
+-------------+-------+--------------+

我想做的基本上是循环遍历这个结果,运行以下命令:

EXEC msdb.dbo.sp_send_dbmail
@recipients= 'email1',     --email address from above query
@subject='Test email',
@body='You have X projects waiting'    -- where X is the COUNT() term

对于每一行。

我的理解是,如果我使用游标,我可以对每个条目进行一些简单的操作,但是,我发现的所有文档和 Stack Overflow 结果都强烈表明这不是一个好策略。

做这样的事情最好的方法是什么?

【问题讨论】:

    标签: sql sql-server sql-server-2008 stored-procedures


    【解决方案1】:

    通常不鼓励使用游标的原因是有一些方法可以在不使用游标的情况下做你想做的事。许多开发人员从过程语言开始,因此循环是常见且自然的。考虑基于集合的操作并不“自然”,因此使用光标来模拟循环。

    在这种情况下,使用光标是合适的,因为没有基于集合的方式来发送单个电子邮件。

    直接从您的数据库服务器发送电子邮件是否是一个好主意是另一个问题......

    【讨论】:

    • 这是使用游标完全合适的时候之一。将查询输出转储到临时表中并对其进行迭代。
    • “基于集合的操作”是什么意思?
    【解决方案2】:

    假设这是进入某个临时表或表变量,您可以向该结果集添加一个行号,如下所示:

    SELECT ROW_NUMBER() OVER (ORDER BY ProjectCodes.ProjectCode) RowNumber, ProjectCodes.ProjectCode, COUNT(Projects.ProjectsID),  ProjectApprovers.EmailApprover
    ...
    

    然后,使用 while 循环,遍历该临时表并获取执行 SP 所需的值,按行号匹配。

    DECLARE @i int = 0
    DECLARE @count int = -- length of query results
    
    WHILE (@i < @count)
    BEGIN
        SELECT @emailAddress = EmailApprover, ...
        FROM @YourTempResults
        WHERE RowNumber = (@i + 1) -- row number is 1-based
    
        EXEC msdb.dbo.sp_send_dbmail @recipients = @emailAddress, ...
    
        SET @i = @i + 1
    END
    

    虽然我们在这里没有做任何繁重的工作,所以在这种情况下我不一定建议不要使用光标。除了它已经这么久了,我不得不刷新如何编写代码。 :)

    【讨论】:

    • 另一个选项是创建一个包含查询中所有信息的表,并根据简单的 Case 语句执行发送的电子邮件过程。
    【解决方案3】:

    您可以定义一个函数,使用您想要的参数发送邮件。然后对您的查询进行选择,其中选择调用该函数。

    Select SendPMMail(ProjectCode, EmailAddress, ProjectCount) from
    (SELECT ProjectCodes.ProjectCode as ProjectCode, 
            COUNT(Projects.ProjectsID) as ProjectCount, 
            ProjectApprovers.EmailApprover as EmailAddress
       FROM Projects 
      INNER JOIN ProjectCodes
            ON Projects.ProjectCodesID=ProjectCodes.ProjectCodesID
      INNER JOIN ProjectApprovers
            ON Projects.ProjectCodesID=ProjectApprovers.ProjectCodesID
      WHERE ProjectApprovers.IsPrimaryApprover=1
      GROUP BY ProjectCodes.ProjectCode, ProjectApprovers.EmailApprover)
    

    【讨论】:

    • 您也可以在一个查询中完成所有操作,但我更喜欢这种方式,因为我发现它更具可读性......我相信其他人不同意。
    【解决方案4】:

    你可以这样做:

    创建一个SQL AGENT JOB:这将每周每 2 次调用您的存储过程(取决于您的需要)

    • 您可以在 SQL Server 的所有底部的 SSMS 上看到此选项并配置设置

    您可以通过存储过程控制向他们发送什么或向谁发送。

    【讨论】:

    • 他的问题是关于如何在结果集中的每一行执行一个 SP,而不是如何安排这样的工作。
    猜你喜欢
    • 1970-01-01
    • 2010-09-14
    • 2010-11-20
    • 2023-02-05
    • 1970-01-01
    • 2011-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多