1

我需要通过电子邮件将 CLR 存储过程的结果(我无法更改 sproc / clr 程序集)作为 HTML 发送。是否有可能在不使用临时表或其他类型的持久化的情况下捕获和格式化存储过程的结果集(通过电子邮件发送)?

4

2 回答 2

2
  USE msdb
  EXEC sp_send_dbmail
  @profile_name = 'MailProfile1',  --you will need to create this profile in the Database Mail under Management
  @recipients = 'test@email.com',
  @subject = 'CLR Sproc Resultset',
  @body = 'Resultset is attached.',
  @execute_query_database = '[DatabaseName]',
  @query = 'exec [DatabaseName].[SchemaName].[CLRProcName]'  

试试这个,希望它能得到你需要的东西。

于 2015-04-17T14:33:46.270 回答
2

我在工作中使用Ola Hallengren 的 SQL Server 维护解决方案进行数据库备份和索引优化。我写了几个不同的存储过程来在每晚工作完成时生成电子邮件,这样我就可以一眼看出每个数据库备份花了多长时间,重建了多少索引和统计信息以及在哪些表上等等。

我为索引和统计信息编写的存储过程如下。

您必须修改它以满足您自己的需求和源数据,但作为发送 HTML 电子邮件的模板,它应该适用于任何事情。

由于您有一个 CLR 存储过程,因此您必须创建一个与 SP 返回具有相同架构的临时表,然后执行 INSERT EXEC 否则您将无法使用我的代码。

CREATE PROCEDURE dbo.spCommandLogIndexRebuildTimePerDatabase

    @Operator   sysname

AS
BEGIN
    SET NOCOUNT ON;

    /* Debug Block
    DECLARE @Operator   sysname = 'Your Operator Name';
    --*/;

    DECLARE @MaxID          int
    ,       @xml            nvarchar(MAX)
    ,       @body           nvarchar(MAX)
    ,       @subj           nvarchar(255)   = N'Index Optimise Results: ' + CAST(CAST(SYSDATETIME() AS date) AS nvarchar) + N' (' + @@SERVERNAME + N')'
    ,       @span_start     nchar(31)       = N'<span style="font-weight:bold">'
    ,       @span_end       nchar(7)        = N'</span>'
    ,       @email          varchar(255);

    -- drop temp table
    BEGIN
        IF  OBJECT_ID('tempdb..#Temp') IS NOT NULL
            DROP TABLE #Temp;
    END

    -- create temp table
    BEGIN
        CREATE TABLE #Temp
        (
            ID                  int     NOT NULL    IDENTITY    PRIMARY KEY
        ,   [Database]          sysname
        ,   [Indexes]           int
        ,   [Statistics]        int
        ,   [TotalDuration]     decimal(19, 3)
        ,   [Time]              time
        );
    END;

    -- fill temp table
    BEGIN
        -- get the starting ID of the latest group of backups
        WITH        CTEBaseData
        AS
        (
                    SELECT      l.ID
                    ,           l.CommandType
                    ,           l.DatabaseName
                    ,           l.StartTime
                    ,           l.EndTime
                    ,           DATEDIFF(MILLISECOND, l.StartTime, l.EndTime) AS DurationMS
                    ,           ROW_NUMBER() OVER (ORDER BY l.StartTime) AS RowNum

                    FROM        dbo.CommandLog l

                    WHERE       l.CommandType IN (N'ALTER_INDEX', N'UPDATE_STATISTICS')
        )

        SELECT      @MaxID = MAX(a.ID)

        FROM        CTEBaseData a

                    LEFT JOIN CTEBaseData b
                        ON a.RowNum = b.RowNum + 1

        WHERE       DATEDIFF(SECOND, ISNULL(b.EndTime, '2013-01-01'), a.StartTime) > 3600;

        -- fill the temp table
        WITH        CTEObjectTimes
        AS
        (
                    SELECT      l.DatabaseName AS [Database]
                    ,           CASE l.CommandType WHEN N'ALTER_INDEX' THEN 1 ELSE 0 END AS [Indexes]
                    ,           CASE l.CommandType WHEN N'UPDATE_STATISTICS' THEN 1 ELSE 0 END AS [Statistics]
                    ,           DATEDIFF(MILLISECOND, l.StartTime, l.EndTime) AS [Milliseconds]

                    FROM        dbo.CommandLog l

                    WHERE       l.CommandType IN (N'ALTER_INDEX', N'UPDATE_STATISTICS')
                                AND l.ID >= @MaxID
        )
        ,           CTEIndividualTotals
        AS
        (
                    SELECT      c.[Database]
                    ,           SUM(c.[Indexes]) AS [Indexes]
                    ,           SUM(c.[Statistics]) AS [Statistics]
                    ,           SUM(c.[Milliseconds]) AS [Milliseconds]

                    FROM        CTEObjectTimes c

                    GROUP BY    c.[Database]
        )
        ,           CTEResult
        AS
        (
                    SELECT      c.[Database]
                    ,           c.[Indexes]
                    ,           c.[Statistics]
                    ,           c.[Milliseconds]
                    ,           0 AS SortOrder

                    FROM        CTEIndividualTotals c

                    UNION ALL

                    SELECT      N'Total'
                    ,           SUM(c.[Indexes])
                    ,           SUM(c.[Statistics])
                    ,           SUM(c.Milliseconds)
                    ,           1

                    FROM        CTEIndividualTotals c
        )

        INSERT      #Temp
        (
                    [Database]
        ,           [Indexes]
        ,           [Statistics]
        ,           [TotalDuration]
        ,           [Time]
        )

        SELECT      c.[Database]
        ,           c.[Indexes]
        ,           c.[Statistics]
        ,           CONVERT(decimal(19, 3), c.[Milliseconds] / 1000.00)
        ,           CONVERT(time, DATEADD(MILLISECOND, c.[Milliseconds], 0))

        FROM        CTEResult c

        ORDER BY    [SortOrder]
        ,           [Database];
    END;

    -- convert temp table to html table
    SELECT  @xml =  CONVERT
                    (
                        nvarchar(MAX)
                    ,   (
                            SELECT  CASE t.[Database] WHEN N'Total' THEN @span_start + t.[Database] + @span_end ELSE t.[Database] END AS [td]
                            ,       N''
                            ,       N'right' AS [td/@align]
                            ,       CASE t.[Database] WHEN N'Total' THEN @span_start + CONVERT(nvarchar(10), t.[Indexes]) + @span_end ELSE CONVERT(nvarchar(10), t.[Indexes]) END AS [td]
                            ,       N''
                            ,       N'right' AS [td/@align]
                            ,       CASE t.[Database] WHEN N'Total' THEN @span_start + CONVERT(nvarchar(10), t.[Statistics]) + @span_end ELSE CONVERT(nvarchar(10), t.[Statistics]) END AS [td]
                            ,       N''
                            ,       N'right' AS [td/@align]
                            ,       CASE t.[Database] WHEN N'Total' THEN @span_start ELSE '' END
                                    + LEFT(CONVERT(nvarchar(50), t.[Time]), 2) + N'h ' + SUBSTRING(CONVERT(nvarchar(50), t.[Time]), 4, 2) + N'm ' + SUBSTRING(CONVERT(nvarchar(50), t.[Time]), 7, 6) + N's'
                                    + CASE t.[Database] WHEN N'Total' THEN @span_end ELSE '' END AS [td]

                            FROM    #Temp t

                            FOR XML PATH('tr')
                                ,   ELEMENTS
                        )
                    );

    -- combine the table rows from above into a complete html document
    SELECT  @body = N'<html><body><H3>Index Optimise Results for '
                    + @@SERVERNAME
                    + N' on '
                    + CONVERT(nvarchar(10), SYSDATETIME(), 120)
                    + N'</H3><table border = 1><tr><th> Database </th><th> Indexes </th><th> Statistics </th><th> Total Time </th></tr>'
                    + REPLACE(REPLACE(@xml, '&lt;', '<'), '&gt;', '>')
                    + N'</table></body></html>';

    -- get the email address of the operator
    SELECT  @email = o.email_address
    FROM    msdb.dbo.sysoperators o
    WHERE   o.name = @Operator;

    -- just in case the operator is non-existent
    SELECT  @email = ISNULL(@email, 'your.fallback.email.account@your.domain.com');

    /* Debug Block
    SELECT  *
    FROM    #Temp;

    SELECT  @Body AS Body
    ,       @email AS Email;
    --*/;

    -- send the email
    EXEC    msdb.dbo.sp_send_dbmail
            @profile_name = N'Database Mail Account'
    ,       @recipients = @email
    ,       @subject = @subj
    ,       @body = @body
    ,       @body_format = 'HTML';
END;
GO

如果您对此有任何疑问,请不要犹豫!

于 2015-04-17T15:10:28.793 回答