¡¡Hola, chicos!!
En este post vengo a traer una solución a un problema que veo casi todos los días en los grupos de Whatsapp y Telegram: Enviar el contenido de una o más tablas o consultas en el cuerpo de un correo electrónico como HTML.

El procedimiento que vamos a utilizar en esta publicación para tomar el contenido de la tabla y transformarlo en HTML ya lo había compartido en el artículo. Cómo exportar datos de una tabla de SQL Server a HTML, pero veo que cada vez que enviaba el artículo, la gente todavía tenía dificultades para enviar el correo electrónico, así que decidí crear este artículo para resolver esta duda de una vez por todas.

Si no sabes cómo configurar Database Mail para comenzar a enviar correos electrónicos a través de SQL Server o tienes dificultades para enviar correos electrónicos simples, accede al artículo SQL Server – Cómo activar y configurar el correo de base de datos para enviar y monitorear correos electrónicos a través de la base de datos (sp_send_dbmail).

Usando sp_send_dbmail y el parámetro @query

Probablemente una de las formas más utilizadas para enviar el resultado de una tabla por correo electrónico es utilizando el parámetro @query de sp_send_dbmail.

Ejemplo:

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 puedes ver, aunque es sumamente sencillo de utilizar, el resultado es muy pobre para la visualización de datos.

Usando el propio sp_send_dbmail y el parámetro @attach_query_result_as_file

Intentaré enviar este contenido como archivo adjunto para ver si mejora. Quizás al exportar a un archivo, el resultado será un poco mejor...

Ejemplo:

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:

Otro muy mal resultado... No mejoró mucho.

Usando XML para crear una variable con el resultado en HTML

Usemos otro enfoque para intentar exportar los datos de la tabla y enviarlos por correo electrónico para que sea fácil verlos.

Ejemplo:

-- 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:

¡¡Ups!! ¡Ya está mejorando mucho! Ahora voy a personalizarlo un poco sólo para mostrarte que puedes jugar con el diseño de los mensajes.

Ejemplo:

-- 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:

Este correo electrónico está quedando muy bien, ¿eh? ¿Qué pasa si quiero enviar más de una tabla en el mismo correo electrónico? ¿Hay alguna manera???

Ejemplo:

-- 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 fácil: stpExporta_Table_HTML_Output

Pensando en facilitar el envío del contenido de una tabla o consulta vía correo electrónico como HTML, decidí crear el Procedimiento Almacenado stpExporta_Table_HTML_Output para automatizar la transformación del resultado de una tabla o consulta a HTML y poder utilizar este para crear un archivo o enviar el HTML generado como cuerpo de un correo electrónico.

Este procedimiento ya había sido compartido en el artículo. Cómo exportar datos de una tabla de SQL Server a HTML, pero decidí compartirlo nuevamente aquí, centrándome en el envío de correos electrónicos HTML.

stpExporta_Table_HTML_Código de salida:
Haga clic aquí para ver el código fuente

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

Su uso es muy sencillo:

-- 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:

Si quiero cambiar la alineación horizontal, simplemente use el 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:

Si desea utilizar el resultado de una consulta en lugar del contenido de una tabla completa, simplemente utilice una tabla temporal global (##table), ya que la tabla temporal local (#table) no 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)

Y para enviar el mismo correo electrónico que en el ejemplo usando XML, se vería así:

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:

¡Y eso es todo, amigos!
Espero que te haya gustado este consejo y nos vemos en el próximo post.