Fala pessoal!!
Neste post eu venho trazer uma solução para um problema que quase todos os dias eu vejo nos grupos de Whatsapp e Telegram: Enviar o conteúdo de uma ou mais tabelas ou queries no corpo de um e-mail como HTML.

A procedure que vamos utilizar nesse post para pegar o conteúdo da tabela e transformar para HTML eu já havia compartilhado no artigo Como exportar dados de uma tabela do SQL Server para HTML, mas vejo que sempre que enviava o artigo, as pessoas ainda tinham dificuldade de enviar o e-mail, então resolvi criar esse artigo para sanar essa dúvida de uma vez por todas.

Se você não sabe como configurar o Database Mail para começar a enviar e-mails pelo SQL Server ou está com dificuldades em enviar e-mails simples, acesse o artigo SQL Server – Como ativar e configurar o Database mail para enviar e monitorar e-mails pelo banco de dados (sp_send_dbmail).

Utilizando a própria sp_send_dbmail e o parâmetro @query

Provavelmente, uma das formas mais utilizadas para enviar o resultado de uma tabela por e-mail é utilizando o parâmetro @query da sp_send_dbmail.

Exemplo:

EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'ProfileEnvioEmail', -- sysname
    @recipients = '[email protected]', -- varchar(max)
    @subject = N'Teste', -- nvarchar(255)
    @body = N'Teste', -- nvarchar(max)
    @body_format = 'html',

    -- Parâmetros específicos do envio da query
    @query = N'select top 20 * from [dirceuresende].[dbo].[Clientes]', -- nvarchar(max)
    @query_result_header = 1, -- bit
    @exclude_query_output = 1, -- bit
    @query_result_width = 50, -- int
    @query_result_separator = '|' -- char(1)

Resultado:

Como vocês podem perceber, embora seja extremamente simples de se utilizar, o resultado é bem ruim para visualização dos dados..

Utilizando a própria sp_send_dbmail e o parâmetro @attach_query_result_as_file

Vou tentar enviando esse conteúdo como anexo para ver se melhora. Talvez exportando pra arquivo, a saíde fique um pouco melhor..

Exemplo:

EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'ProfileEnvioEmail', -- sysname
    @recipients = '[email protected]', -- varchar(max)
    @subject = N'Teste', -- nvarchar(255)
    @body = N'Teste', -- nvarchar(max)
    @body_format = 'html',

    -- Parâmetros específicos do envio da query
    @query = N'select top 20 * from [dirceuresende].[dbo].[Clientes]', -- nvarchar(max)
    @query_result_header = 1, -- bit
    @exclude_query_output = 1, -- bit
    @query_result_width = 50, -- int
    @query_result_separator = '|', -- char(1)

    -- Transformando em arquivo
    @attach_query_result_as_file = 1,
    @query_attachment_filename = 'arquivo.txt'

Resultado:

Mais um resultado bem ruim… Não melhorou muito.

Utilizando XML para criar uma variável com o resultado em HTML

Vamos utilizar outra abordagem então para tentar exportar os dados da tabela e enviar em um e-mail de forma que fique fácil de visualizar os dados.

Exemplo:

-- Transforma o conteúdo da query em HTML
DECLARE @HTML VARCHAR(MAX);  

SET @HTML = '
<table border="1">
    <tr>
        <th>Codigo</th>
        <th>Nome</th>
        <th>Ativo?</th>
    </tr>' +  
    CAST ( 
    (
        SELECT TOP 20 
            td = Codigo, '',
            td = Nome, '',
            td = Ativo
        FROM [dirceuresende].[dbo].[Clientes]
        FOR XML PATH('tr'), TYPE
    ) AS NVARCHAR(MAX) ) + '
</table>';


-- Envia o e-mail
EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'ProfileEnvioEmail', -- sysname
    @recipients = '[email protected]', -- varchar(max)
    @subject = N'Teste', -- nvarchar(255)
    @body = @HTML, -- nvarchar(max)
    @body_format = 'html'

Resultado:

Opa!! Já tá ficando bem melhor! Agora vou personalizar um pouco só pra mostrar a vocês que dá pra brincar com o design das mensagens.

Exemplo:

-- Transforma o conteúdo da query em HTML
DECLARE @HTML VARCHAR(MAX);  

SET @HTML = '
<html>
<head>
    <title>Titulo</title>
    <style type="text/css">
        table { padding:0; border-spacing: 0; border-collapse: collapse; }
        thead { background: #00B050; border: 1px solid #ddd; }
        th { padding: 10px; font-weight: bold; border: 1px solid #000; color: #fff; }
        tr { padding: 0; }
        td { padding: 5px; border: 1px solid #cacaca; margin:0; text-align: center; }
    </style>
</head>

<h2>Lista de Usuários do Sistema</h2>
Veja a lista dos usuários permitidos no sistema:<br/><br/>

<table>
    <thead>
        <tr>
            <th>Codigo</th>
            <th>Nome</th>
            <th>Ativo?</th>
        </tr>
    </thead>
    
    <tbody>' +  
    CAST ( 
    (
        SELECT TOP 20 
            td = Codigo, '',
            td = Nome, '',
            td = Ativo
        FROM [dirceuresende].[dbo].[Clientes]
        FOR XML PATH('tr'), TYPE
    ) AS NVARCHAR(MAX) ) + '
    </tbody>
</table>

<br/><br/>
Em caso de dúvidas, favor entrar em contato<br/><br/>
Atenciosamente,<br/>

<img src="https://www.sqlsaturday.com/images/speakers/6984-e5594a7e.jpg" /><br/>
DBA';


-- Envia o e-mail
EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'ProfileEnvioEmail', -- sysname
    @recipients = '[email protected]', -- varchar(max)
    @subject = N'Teste', -- nvarchar(255)
    @body = @HTML, -- nvarchar(max)
    @body_format = 'html'

Resultado:

Tá ficando bem maneiro esse e-mail, hein?? E se eu quiser enviar mais de uma tabela no mesmo e-mail? Tem como???

Exemplo:

-- Transforma o conteúdo da query em HTML
DECLARE @HTML VARCHAR(MAX);  

SET @HTML = '
<html>
<head>
    <title>Titulo</title>
    <style type="text/css">
        table { padding:0; border-spacing: 0; border-collapse: collapse; }
        thead { background: #00B050; border: 1px solid #ddd; }
        th { padding: 10px; font-weight: bold; border: 1px solid #000; color: #fff; }
        tr { padding: 0; }
        td { padding: 5px; border: 1px solid #cacaca; margin:0; text-align: center; }
    </style>
</head>

<h2>Lista de Usuários do Sistema</h2>
Veja a lista dos usuários permitidos no sistema:<br/><br/>

<table>
    <thead>
        <tr>
            <th>Codigo</th>
            <th>Nome</th>
            <th>Ativo?</th>
        </tr>
    </thead>
    
    <tbody>' +  
    CAST ( 
    (
        SELECT TOP 20 
            td = Codigo, '',
            td = Nome, '',
            td = Ativo
        FROM [dirceuresende].[dbo].[Clientes]
        FOR XML PATH('tr'), TYPE
    ) AS NVARCHAR(MAX) ) + '
    </tbody>
</table>

<br/><br/>
<h2>Lista de dos Objetos no Banco</h2>
Veja a lista dos objetos que existem nesse banco:<br/><br/>

<table>
    <thead>
        <tr>
            <th>Nome</th>
            <th>ID do Objeto</th>
            <th>Data de Criação</th>
            <th>Objeto de Sistema?</th>
            <th>Lock Escalation</th>
            <th>Durability</th>
            <th>Temporal Type</th>
        </tr>
    </thead>
    
    <tbody>' +  

    
    CAST ( 
    (
        SELECT TOP 20 
            td = [name] , '',
            td = [object_id], '',
            td = CONVERT(VARCHAR(19), create_date, 103), '',
            td = (CASE WHEN is_ms_shipped = 1 THEN 'true' ELSE 'false' END), '',
            td = lock_escalation_desc, '',
            td = durability_desc, '',
            td = temporal_type_desc
        FROM dirceuresende.sys.tables
        FOR XML PATH('tr'), TYPE
    ) AS NVARCHAR(MAX) ) + '
    </tbody>
</table>

<br/><br/>
Em caso de dúvidas, favor entrar em contato<br/><br/>
Atenciosamente,<br/>

<img src="https://www.sqlsaturday.com/images/speakers/6984-e5594a7e.jpg" /><br/>
DBA';


-- Envia o e-mail
EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'ProfileEnvioEmail', -- sysname
    @recipients = '[email protected]', -- varchar(max)
    @subject = N'Teste', -- nvarchar(255)
    @body = @HTML, -- nvarchar(max)
    @body_format = 'html'

Resultado:

Modo Easy: stpExporta_Tabela_HTML_Output

Pensando em facilitar o envio do conteúdo de uma tabela ou query por e-mail como HTML, resolvi criar a Stored Procedure stpExporta_Tabela_HTML_Output para automatizar a transformação do resultado de uma tabela ou query para HTML e poder usar isso para criar um arquivo ou enviar o HTML gerado como o corpo de um e-mail.

Essa procedure já havia sido compartilhada no artigo Como exportar dados de uma tabela do SQL Server para HTML, mas resolvi compartilhar de novo aqui, com foco em envio de e-mail HTML.

Código da stpExporta_Tabela_HTML_Output:
Clique aqui para visualizar o código-fonte

CREATE PROCEDURE [dbo].[stpExporta_Tabela_HTML_Output]
    @Ds_Tabela [varchar](max),
    @Fl_Aplica_Estilo_Padrao BIT = 1,
    @Ds_Alinhamento VARCHAR(10) = 'left',
    @Ds_OrderBy VARCHAR(MAX) = '',
    @Ds_Saida VARCHAR(MAX) OUTPUT
AS
BEGIN
    
    
    SET NOCOUNT ON
    
    
    DECLARE
        @query NVARCHAR(MAX),
        @Database sysname,
        @Nome_Tabela sysname

    
    
    IF (LEFT(@Ds_Tabela, 1) = '#')
    BEGIN
        SET @Database = 'tempdb.'
        SET @Nome_Tabela = @Ds_Tabela
    END
    ELSE BEGIN
        SET @Database = LEFT(@Ds_Tabela, CHARINDEX('.', @Ds_Tabela))
        SET @Nome_Tabela = SUBSTRING(@Ds_Tabela, LEN(@Ds_Tabela) - CHARINDEX('.', REVERSE(@Ds_Tabela)) + 2, LEN(@Ds_Tabela))
    END

    
    SET @query = '
    SELECT ORDINAL_POSITION, COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, NUMERIC_SCALE
    FROM ' + @Database + 'INFORMATION_SCHEMA.COLUMNS 
    WHERE TABLE_NAME = ''' + @Nome_Tabela + '''
    ORDER BY ORDINAL_POSITION'
    
    
    IF (OBJECT_ID('tempdb..#Colunas') IS NOT NULL) DROP TABLE #Colunas
    CREATE TABLE #Colunas (
        ORDINAL_POSITION int, 
        COLUMN_NAME sysname, 
        DATA_TYPE nvarchar(128), 
        CHARACTER_MAXIMUM_LENGTH int,
        NUMERIC_PRECISION tinyint, 
        NUMERIC_SCALE int
    )

    INSERT INTO #Colunas
    EXEC(@query)

    
    
    IF (@Fl_Aplica_Estilo_Padrao = 1)
    BEGIN
    
    SET @Ds_Saida = '<html>
<head>
    <title>Titulo</title>
    <style type="text/css">
        table { padding:0; border-spacing: 0; border-collapse: collapse; }
        thead { background: #00B050; border: 1px solid #ddd; }
        th { padding: 10px; font-weight: bold; border: 1px solid #000; color: #fff; }
        tr { padding: 0; }
        td { padding: 5px; border: 1px solid #cacaca; margin:0; text-align:' + @Ds_Alinhamento + '; }
    </style>
</head>'
    
    END
    
    
    
    SET @Ds_Saida = ISNULL(@Ds_Saida, '') + '
<table>
    <thead>
        <tr>'


    -- Cabeçalho da tabela
    DECLARE 
        @contadorColuna INT = 1, 
        @totalColunas INT = (SELECT COUNT(*) FROM #Colunas), 
        @nomeColuna sysname,
        @tipoColuna sysname
    

    WHILE(@contadorColuna <= @totalColunas)
    BEGIN

        SELECT @nomeColuna = COLUMN_NAME
        FROM #Colunas
        WHERE ORDINAL_POSITION = @contadorColuna


        SET @Ds_Saida = ISNULL(@Ds_Saida, '') + '
            <th>' + @nomeColuna + '</th>'


        SET @contadorColuna = @contadorColuna + 1

    END



    SET @Ds_Saida = ISNULL(@Ds_Saida, '') + '
        </tr>
    </thead>
    <tbody>'


    
    -- Conteúdo da tabela

    DECLARE @saida VARCHAR(MAX)

    SET @query = '
SELECT @saida = (
    SELECT '


    SET @contadorColuna = 1

    WHILE(@contadorColuna <= @totalColunas)
    BEGIN

        SELECT 
            @nomeColuna = COLUMN_NAME,
            @tipoColuna = DATA_TYPE
        FROM 
            #Colunas
        WHERE 
            ORDINAL_POSITION = @contadorColuna



        IF (@tipoColuna IN ('int', 'bigint', 'float', 'numeric', 'decimal', 'bit', 'tinyint', 'smallint', 'integer'))
        BEGIN
        
            SET @query = @query + '
    ISNULL(CAST([' + @nomeColuna + '] AS VARCHAR(MAX)), '''') AS [td]'
    
        END
        ELSE BEGIN
        
            SET @query = @query + '
    ISNULL([' + @nomeColuna + '], '''') AS [td]'
    
        END
    
        
        IF (@contadorColuna < @totalColunas)
            SET @query = @query + ','

        
        SET @contadorColuna = @contadorColuna + 1

    END



    SET @query = @query + '
FROM ' + @Ds_Tabela + (CASE WHEN ISNULL(@Ds_OrderBy, '') = '' THEN '' ELSE ' 
ORDER BY ' END) + @Ds_OrderBy + '
FOR XML RAW(''tr''), Elements
)'
    
    
    EXEC tempdb.sys.sp_executesql
        @query,
        N'@saida NVARCHAR(MAX) OUTPUT',
        @saida OUTPUT


    -- Identação
    SET @saida = REPLACE(@saida, '<tr>', '
        <tr>')

    SET @saida = REPLACE(@saida, '<td>', '
            <td>')

    SET @saida = REPLACE(@saida, '</tr>', '
        </tr>')


    SET @Ds_Saida = ISNULL(@Ds_Saida, '') + @saida


    
    SET @Ds_Saida = ISNULL(@Ds_Saida, '') + '
    </tbody>
</table>'
    
            
END
GO

O seu uso é bem simples:

-- Transforma o conteúdo da query em HTML
DECLARE @HTML VARCHAR(MAX)

EXEC dbo.stpExporta_Tabela_HTML_Output
    @Ds_Tabela = 'dirceuresende.dbo.Clientes', -- varchar(max)
    @Ds_Saida = @HTML OUTPUT -- varchar(max)


-- Envia o e-mail
EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'ProfileEnvioEmail', -- sysname
    @recipients = '[email protected]', -- varchar(max)
    @subject = N'Teste', -- nvarchar(255)
    @body = @HTML, -- nvarchar(max)
    @body_format = 'html'

Resultado:

Caso eu queira trocar o alinhamento horizontal, basta utilizar o parâmetro @Ds_Alinhamento:

-- Transforma o conteúdo da query em HTML
DECLARE @HTML VARCHAR(MAX)

EXEC dbo.stpExporta_Tabela_HTML_Output
    @Ds_Tabela = 'dirceuresende.dbo.Clientes', -- varchar(max)
    @Ds_Saida = @HTML OUTPUT, -- varchar(max)
    @Ds_Alinhamento = 'center' -- parâmetros: left, center e right


-- Envia o e-mail
EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'ProfileEnvioEmail', -- sysname
    @recipients = '[email protected]', -- varchar(max)
    @subject = N'Teste', -- nvarchar(255)
    @body = @HTML, -- nvarchar(max)
    @body_format = 'html'

Resultado:

Caso você queira utilizar o resultado de uma query ao invés do conteúdo de uma tabela inteira, basta utilizar uma tabela temporária global (##tabela), pois a temporária local (#tabela) não irá funcionar:

-- Cria uma nova tabela com o resultado da query desejada
IF (OBJECT_ID('tempdb..##Teste') IS NOT NULL) DROP TABLE ##Teste
SELECT Codigo, Nome
INTO ##Teste
FROM dirceuresende.dbo.Clientes
WHERE Codigo > 2


-- Transforma o conteúdo da query em HTML
DECLARE @HTML VARCHAR(MAX)

EXEC dbo.stpExporta_Tabela_HTML_Output
    @Ds_Tabela = '##Teste', -- varchar(max)
    @Ds_Saida = @HTML OUTPUT -- varchar(max)

E para enviar o mesmo e-mail do exemplo utilizando o XML, ficaria assim:

DECLARE
    @HTML VARCHAR(MAX),
    @HTML1 VARCHAR(MAX),
    @HTML2 VARCHAR(MAX)
    

-- Transforma o conteúdo da query em HTML
EXEC dbo.stpExporta_Tabela_HTML_Output
    @Ds_Tabela = 'dirceuresende.dbo.Clientes', -- varchar(max)
    @Ds_Saida = @HTML1 OUTPUT -- varchar(max)

    

IF (OBJECT_ID('tempdb..##Tabela2') IS NOT NULL) DROP TABLE ##Tabela2
SELECT TOP 20 
    [name],
    [object_id],
    CONVERT(VARCHAR(19), create_date, 103) AS create_date,
    (CASE WHEN is_ms_shipped = 1 THEN 'true' ELSE 'false' END) AS is_ms_shipped,
    lock_escalation_desc,
    durability_desc,
    temporal_type_desc
INTO
    ##Tabela2
FROM
    dirceuresende.sys.tables


EXEC dbo.stpExporta_Tabela_HTML_Output
    @Ds_Tabela = '##Tabela2', -- varchar(max)
    @Ds_Saida = @HTML2 OUTPUT, -- varchar(max)
    @Fl_Aplica_Estilo_Padrao = 0 -- Utilizar 0 se for utilizar a Procedure mais de 1x


SET @HTML = '
<h2>Lista de Usuários do Sistema</h2>
Veja a lista dos usuários permitidos no sistema:<br/><br/>' + ISNULL(@HTML1, '') + '

<br/><br/>
<h2>Lista de dos Objetos no Banco</h2>
Veja a lista dos objetos que existem nesse banco:<br/><br/>' + ISNULL(@HTML2, '') + '


<br/><br/>
Em caso de dúvidas, favor entrar em contato<br/><br/>
Atenciosamente,<br/>

<img src="https://www.sqlsaturday.com/images/speakers/6984-e5594a7e.jpg" /><br/>
DBA';


-- Envia o e-mail
EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'ProfileEnvioEmail', -- sysname
    @recipients = '[email protected]', -- varchar(max)
    @subject = N'Teste', -- nvarchar(255)
    @body = @HTML, -- nvarchar(max)
    @body_format = 'html'

Resultado:

E é isso aí, pessoal!
Espero que tenham gostado dessa dica e até o próximo post.