Konvertieren Sie eine SQL-Abfrageergebnistabelle in eine HTML-Tabelle für E-Mail
Lesezeit: 15 Minuten
Ich führe eine SQL-Abfrage aus, die eine Ergebnistabelle zurückgibt. Ich möchte die Tabelle in einer E-Mail mit dbo.sp_send_dbMail senden.
Gibt es in SQL eine einfache Möglichkeit, eine Tabelle in eine HTML-Tabelle umzuwandeln? Derzeit konstruiere ich es manuell mit COALESCE und füge die Ergebnisse in ein Varchar ein, das ich als emailBody verwende.
Gibt es einen besseren Weg, dies zu tun?
Vielleicht möchten Sie diese Antwort sehen. Ein XQuer FLWOR basierter Ansatz, der SELECT als Parameter mit Unterstützung für Header, CSS und Hyperlinks verwendet.
– Schnugo
9. Februar 18 um 22:08 Uhr
MgSam
Ich habe eine dynamische Prozedur erstellt, die jede zufällige Abfrage in eine HTML-Tabelle umwandelt, sodass Sie keine Spalten wie in den anderen Antworten fest codieren müssen.
-- Description: Turns a query into a formatted HTML table. Useful for emails.
-- Any ORDER BY clause needs to be passed in the separate ORDER BY parameter.
-- =============================================
CREATE PROC [dbo].[spQueryToHtmlTable]
(
@query nvarchar(MAX), --A query to turn into HTML format. It should not include an ORDER BY clause.
@orderBy nvarchar(MAX) = NULL, --An optional ORDER BY clause. It should contain the words 'ORDER BY'.
@html nvarchar(MAX) = NULL OUTPUT --The HTML output of the procedure.
)
AS
BEGIN
SET NOCOUNT ON;
IF @orderBy IS NULL BEGIN
SET @orderBy = ''
END
SET @orderBy = REPLACE(@orderBy, '''', '''''');
DECLARE @realQuery nvarchar(MAX) = '
DECLARE @headerRow nvarchar(MAX);
DECLARE @cols nvarchar(MAX);
SELECT * INTO #dynSql FROM (' + @query + ') sub;
SELECT @cols = COALESCE(@cols + '', '''''''', '', '''') + ''['' + name + ''] AS ''''td''''''
FROM tempdb.sys.columns
WHERE object_id = object_id(''tempdb..#dynSql'')
ORDER BY column_id;
SET @cols=""SET @html = CAST(( SELECT '' + @cols + '' FROM #dynSql ' + @orderBy + ' FOR XML PATH(''''tr''''), ELEMENTS XSINIL) AS nvarchar(max))''
EXEC sys.sp_executesql @cols, N''@html nvarchar(MAX) OUTPUT'', @html=@html OUTPUT
SELECT @headerRow = COALESCE(@headerRow + '''', '''') + ''<th>'' + name + ''</th>''
FROM tempdb.sys.columns
WHERE object_id = object_id(''tempdb..#dynSql'')
ORDER BY column_id;
SET @headerRow = ''<tr>'' + @headerRow + ''</tr>'';
SET @html=""<table border="1">'' + @headerRow + @html + ''</table>'';
';
EXEC sys.sp_executesql @realQuery, N'@html nvarchar(MAX) OUTPUT', @html=@html OUTPUT
END
GO
Verwandte: Hier ist ein ähnlicher Code, um eine beliebige Abfrage in eine CSV-Zeichenfolge umzuwandeln.
Das ist etwas, wonach ich gesucht habe und was ich schreiben wollte. Dies ist eine flexiblere Version dessen, was Sie normalerweise sehen. Ich habe bearbeitet, um die Zeile für die RealQuery zu reparieren, es ist wirklich eine Zeile, die, wenn jemand sie kopiert und einfügt, nicht so funktioniert, wie sie ist. Guter Code, sehr guter Code.
– joel
23. April 15 um 18:15 Uhr
Dies erzeugt keine Zellen für NULL Werte. Um dies zu beheben, ändern Sie ELEMENTS) zu ELEMENTS XSINIL). Die Nullzellen haben dann das Attribut xsi:nil="true", die möglicherweise durch ein spezielles Styling ersetzt werden könnten. Tolles Verfahren!
– Nathan
24. April 15 um 22:25 Uhr
Exzellent! Wenn Sie eine gespeicherte Prozedur ausführen möchten, können Sie außerdem die Ergebnisse Ihrer gespeicherten Prozedur in eine temporäre Tabelle einfügen und stattdessen aus dieser auswählen
– Ben
5. Mai 15 um 20:14 Uhr
Ausgezeichneter SP, das sollte die beste Antwort sein!
– mdelafuq
24. August 16 um 18:56 Uhr
Vielleicht bin ich nur dumm, aber ich verstehe nicht, wie ich diese Antwort umsetzen soll, selbst nachdem ich die Anweisungen gelesen habe. Sie ergeben für mich keinen Sinn. Ist das Rubin? Binde ich einfach die @query an die Abfragezeichenfolge und führe die Datei aus? Es wäre schön, eine idiotenfreundliche Anleitung zu haben.
declare @body varchar(max)
set @body = cast( (
select td = dbtable + '</td><td>' + cast( entities as varchar(30) ) + '</td><td>' + cast( rows as varchar(30) )
from (
select dbtable = object_name( object_id ),
entities = count( distinct name ),
rows = count( * )
from sys.columns
group by object_name( object_id )
) as d
for xml path( 'tr' ), type ) as varchar(max) )
set @body = '<table cellpadding="2" cellspacing="2" border="1">'
+ '<tr><th>Database Table</th><th>Entity Count</th><th>Total Rows</th></tr>'
+ replace( replace( @body, '<', '<' ), '>', '>' )
+ '</table>'
print @body
Sobald du hast @bodykönnen Sie dann einen beliebigen E-Mail-Mechanismus verwenden.
Es ist immer sehr gefährlich zu erstellen XML auf Saitenbasis … Ihr ersetzt < und >aber es gibt noch viel mehr gefährliche Charaktere wie die &. Du kannst nachschauen [this answer] für einen anderen Ansatz.
– Schnugo
9. Februar 18 um 22:10 Uhr
Ich mag den Ansatz. Können Sie die Funktionalität hinzufügen, um bestimmten Zeilen eine Klasse hinzuzufügen (dh jede zweite Zeile, um eine andere CSS-Klasse zu haben)? Ich weiß, dass ich Ihre Lösung abstrahieren kann, um dynamisches SQL zu verwenden, obwohl dies den Zweck zunichte macht.
– Eli
13. März 19 um 16:54 Uhr
@Eli, in keiner Weise würde das als “hacky” angesehen werden, als dies bereits ist.
– Justin Stolle
14. März 19 um 20:53 Uhr
Das könnte Ihnen eine Vorstellung geben –
CREATE TABLE #Temp
(
[Rank] [int],
[Player Name] [varchar](128),
[Ranking Points] [int],
[Country] [varchar](128)
)
INSERT INTO #Temp
SELECT 1,'Rafael Nadal',12390,'Spain'
UNION ALL
SELECT 2,'Roger Federer',7965,'Switzerland'
UNION ALL
SELECT 3,'Novak Djokovic',7880,'Serbia'
DECLARE @xml NVARCHAR(MAX)
DECLARE @body NVARCHAR(MAX)
SET @xml = CAST(( SELECT [Rank] AS 'td','',[Player Name] AS 'td','',
[Ranking Points] AS 'td','', Country AS 'td'
FROM #Temp ORDER BY Rank
FOR XML PATH('tr'), ELEMENTS ) AS NVARCHAR(MAX))
SET @body ='<html><body><H3>Tennis Rankings Info</H3>
<table border = 1>
<tr>
<th> Rank </th> <th> Player Name </th> <th> Ranking Points </th> <th> Country </th></tr>'
SET @body = @body + @xml +'</table></body></html>'
EXEC msdb.dbo.sp_send_dbmail
@profile_name="SQL ALERTING", -- replace with your SQL Database Mail Profile
@body = @body,
@body_format="HTML",
@recipients="[email protected]", -- replace with your email address
@subject="E-mail in Tabular Format" ;
DROP TABLE #Temp
Interessanter Ansatz, obwohl ich nicht verstehe, warum Sie nach jedem Feld die zusätzliche Leerzeichenfolge haben. Können Sie bitte die Funktionalität / Notwendigkeit dieses Stücks erklären? Ein weiteres Stück, das ich brauche, konnte Ihre Lösung daher nicht verwenden, war das Hinzufügen einer Klasse zu bestimmten Zeilen (dh jede zweite Zeile, um eine andere CSS-Klasse zu haben).
– Eli
13. März 19 um 16:52 Uhr
@Eli Ich würde vermuten, dass MS SQL XML gleichnamige Felder zu einem kombiniert. Ein Leerzeichen oder ein Nullwert ohne Namen wird nicht serialisiert und getrennt td Werte in ihre eigenen Spalten und nicht in eine Verkettung.
– Zachary Scott
28. Dezember 2020 um 22:36 Uhr
Hier unten mein häufig verwendetes Skript. Ich verwende dies, um Skripte auf zwei Tabellen/Ansichten mit einem SQL-Job auszuführen und die Ergebnisse als zwei HTML-Tabellen per E-Mail zu senden. Natürlich sollten Sie ein E-Mail-Profil erstellen, bevor Sie dies ausführen.
DECLARE @mailfrom varchar(max)
DECLARE @subject varchar(100)
DECLARE @tableHTML NVARCHAR(MAX), @tableHTML1 NVARCHAR(MAX), @tableHTML2 NVARCHAR(MAX), @mailbody NVARCHAR(MAX)
DECLARE @Table1 NVARCHAR(MAX), @Table2 NVARCHAR(MAX)
DECLARE @jobName varchar(100)
SELECT @jobName = name from msdb..sysjobs where job_id = $(ESCAPE_NONE(JOBID))
-- If the result set is not empty then fill the Table1 HTML table
IF (SELECT COUNT(*) FROM [Database].[Schema].[Table1]) > 0
BEGIN
SET @Table1 = N''
SELECT @Table1 = @Table1 + '<tr style="font-size:13px;background-color:#FFFFFF">' +
'<td>' + ColumnText + '</td>' +
'<td>' + CAST(ColumnNumber as nvarchar(30)) + '</td>' + '</tr>'
FROM [Database].[Schema].[Table1]
ORDER BY ColumnText,ColumnNumber
SET @tableHTML1 =
N'<table border="1" align="Left" cellpadding="2" cellspacing="0" style="color:black;font-family:arial,helvetica,sans-serif;text-align:left;" >' +
N'<tr style ="font-size:13px;font-weight: normal;background: #FFFFFF">
<th align=left>ColumnTextHeader1</th>
<th align=left>ColumnNumberHeader2</th> </tr>' + @Table1 + '</table>'
END
ELSE
BEGIN
SET @tableHTML1 = N''
SET @Table1 = N''
END
-- If the result set is not empty then fill the Table2 HTML table
IF (SELECT COUNT(*) FROM [Database].[Schema].[Table2]) > 0
BEGIN
SET @Table2 = N''
SELECT @Table2 = @Table2 + '<tr style="font-size:13px;background-color:#FFFFFF">' +
'<td>' + ColumnText + '</td>' +
'<td>' + CAST(ColumnNumber as nvarchar(30)) + '</td>' + '</tr>'
FROM [Database].[Schema].[Table2]
ORDER BY ColumnText,ColumnNumber
SET @tableHTML2 =
N'<table border="1" align="Left" cellpadding="2" cellspacing="0" style="color:black;font-family:arial,helvetica,sans-serif;text-align:left;" >' +
N'<tr style ="font-size:13px;font-weight: normal;background: #FFFFFF">
<th align=left>ColumnTextHeader1</th>
<th align=left>ColumnNumberHeader2</th> </tr>' + @Table2 + '</table>'
END
ELSE
BEGIN
SET @tableHTML2 = N''
SET @Table2 = N''
END
SET @tableHTML = @tableHTML1 + @tableHTML2
-- If result sets from Table1 and Table2 are empty, then don't sent mail.
IF (SELECT @tableHTML) <> ''
BEGIN
SET @mailbody = N' Write mail text here<br><br>' + @tableHTML
SELECT @mailfrom = 'SQL Server <' + cast(SERVERPROPERTY('ComputerNamePhysicalNETBIOS') as varchar(50)) + '@domain.com>'
SELECT @subject = N'Mail Subject [Job: ' + @jobName + ']'
EXEC msdb.dbo.sp_send_dbmail
@profile_name="mailprofilename",
@recipients="<[email protected]>",
@from_address = @mailfrom,
@reply_to = '<[email protected]>',
@subject = @subject,
@body = @mailbody,
@body_format="HTML"
-- ,@importance="HIGH"
END
basierend auf JustinStolle-Code (danke), wollte ich eine Lösung, die generisch sein könnte, ohne die Spaltennamen angeben zu müssen.
Dieses Beispiel verwendet die Daten einer temporären Tabelle, kann aber natürlich nach Bedarf angepasst werden.
Hier ist, was ich bekommen habe:
DECLARE @htmlTH VARCHAR(MAX) = '',
@htmlTD VARCHAR(MAX)
--get header, columns name
SELECT @htmlTH = @htmlTH + '<TH>' + name + '</TH>' FROM tempdb.sys.columns WHERE object_id = OBJECT_ID('tempdb.dbo.#results')
--convert table to XML PATH, ELEMENTS XSINIL is used to include NULL values
SET @htmlTD = (SELECT * FROM #results FOR XML PATH('TR'), ELEMENTS XSINIL)
--convert the way ELEMENTS XSINIL display NULL to display word NULL
SET @htmlTD = REPLACE(@htmlTD, ' xsi:nil="true"/>', '>NULL</TD>')
SET @htmlTD = REPLACE(@htmlTD, '<TR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">', '<TR>')
--FOR XML PATH will set tags for each column name, <columnName1>abc</columnName1><columnName2>def</columnName2>
--this will replace all the column names with TD (html table data tag)
SELECT @htmlTD = REPLACE(REPLACE(@htmlTD, '<' + name + '>', '<TD>'), '</' + name + '>', '</TD>')
FROM tempdb.sys.columns WHERE object_id = OBJECT_ID('tempdb.dbo.#results')
SELECT '<TABLE cellpadding="2" cellspacing="2" border="1">'
+ '<TR>' + @htmlTH + '</TR>'
+ @htmlTD
+ '</TABLE>'
FA LE TI NR
Angenommen, jemand hat sich hierher verirrt und versteht die Verwendung der markierten Antwort-SQL nicht, bitte lesen Sie meine … es ist bearbeitet und funktioniert. Tabelle:Personal, Spalten:Personalname,PersonalTelefon und PersonalGeburtstag
declare @body varchar(max)
-- Create the body
set @body = cast( (
select td = dbtable + '</td><td>' + cast( phone as varchar(30) ) + '</td><td>' + cast( age as varchar(30) )
from (
select dbtable = StaffName ,
phone = staffphone,
age = datepart(day,staffdob)
from staff
group by staffname,StaffPhone,StaffDOB
) as d
for xml path( 'tr' ), type ) as varchar(max) )
set @body = '<table cellpadding="2" cellspacing="2" border="1">'
+ '<tr><th>Database Table</th><th>Entity Count</th><th>Total Rows</th></tr>'
+ replace( replace( @body, '<', '<' ), '>', '>' )
+ '<table>'
print @body
Sandeep
Ich habe versucht, mehrere Tabellen mit dem obigen Mahesh-Beispiel zu drucken. Veröffentlichung zur Bequemlichkeit anderer
USE MyDataBase
DECLARE @RECORDS_THAT_NEED_TO_SEND_EMAIL TABLE (ID INT IDENTITY(1,1),
POS_ID INT,
POS_NUM VARCHAR(100) NULL,
DEPARTMENT VARCHAR(100) NULL,
DISTRICT VARCHAR(50) NULL,
COST_LOC VARCHAR(100) NULL,
EMPLOYEE_NAME VARCHAR(200) NULL)
INSERT INTO @RECORDS_THAT_NEED_TO_SEND_EMAIL(POS_ID,POS_NUM,DISTRICT,COST_LOC,DEPARTMENT,EMPLOYEE_NAME)
SELECT uvwpos.POS_ID,uvwpos.POS_NUM,uvwpos.DISTRICT, uvwpos.COST_LOC,uvwpos.DEPARTMENT,uvemp.LAST_NAME + ' ' + uvemp.FIRST_NAME
FROM uvwPOSITIONS uvwpos LEFT JOIN uvwEMPLOYEES uvemp
on uvemp.POS_ID=uvwpos.POS_ID
WHERE uvwpos.ACTIVE=1 AND uvwpos.POS_NUM LIKE 'sde%'AND (
(RTRIM(LTRIM(LEFT(uvwpos.DEPARTMENT,LEN(uvwpos.DEPARTMENT)-1))) <> RTRIM(LTRIM(uvwpos.COST_LOC)))
OR (uvwpos.DISTRICT IS NULL)
OR (uvwpos.COST_LOC IS NULL) )
DECLARE @RESULT_DISTRICT_ISEMPTY varchar(4000)
DECLARE @RESULT_COST_LOC_ISEMPTY varchar(4000)
DECLARE @RESULT_COST_LOC__AND_DISTRICT_NOT_MATCHING varchar(4000)
DECLARE @BODY NVARCHAR(MAX)
DECLARE @HTMLHEADER VARCHAR(100)
DECLARE @HTMLFOOTER VARCHAR(100)
SET @HTMLHEADER='<html><body>'
SET @HTMLFOOTER ='</body></html>'
SET @RESULT_DISTRICT_ISEMPTY = '';
SET @BODY =@HTMLHEADER+ '<H3>PositionNumber where District is Empty.</H3>
<table border = 1>
<tr>
<th> POS_ID </th> <th> POS_NUM </th> <th> DEPARTMENT </th> <th> DISTRICT </th> <th> COST_LOC </th></tr>'
SET @RESULT_DISTRICT_ISEMPTY = CAST(( SELECT [POS_ID] AS 'td','',RTRIM([POS_NUM]) AS 'td','',
ISNULL(LEFT(DEPARTMENT,LEN(DEPARTMENT)-1),' ') AS 'td','', ISNULL([DISTRICT],' ') AS 'td','',ISNULL([COST_LOC],' ') AS 'td'
FROM @RECORDS_THAT_NEED_TO_SEND_EMAIL
WHERE DISTRICT IS NULL
FOR XML PATH('tr'), ELEMENTS ) AS VARCHAR(MAX))
SET @BODY = @BODY + @RESULT_DISTRICT_ISEMPTY +'</table>'
DECLARE @RESULT_COST_LOC_ISEMPTY_HEADER VARCHAR(400)
SET @RESULT_COST_LOC_ISEMPTY_HEADER ='<H3>PositionNumber where COST_LOC is Empty.</H3>
<table border = 1>
<tr>
<th> POS_ID </th> <th> POS_NUM </th> <th> DEPARTMENT </th> <th> DISTRICT </th> <th> COST_LOC </th></tr>'
SET @RESULT_COST_LOC_ISEMPTY = CAST(( SELECT [POS_ID] AS 'td','',RTRIM([POS_NUM]) AS 'td','',
ISNULL(LEFT(DEPARTMENT,LEN(DEPARTMENT)-1),' ') AS 'td','', ISNULL([DISTRICT],' ') AS 'td','',ISNULL([COST_LOC],' ') AS 'td'
FROM @RECORDS_THAT_NEED_TO_SEND_EMAIL
WHERE COST_LOC IS NULL
FOR XML PATH('tr'), ELEMENTS ) AS VARCHAR(MAX))
SET @BODY = @BODY + @RESULT_COST_LOC_ISEMPTY_HEADER+ @RESULT_COST_LOC_ISEMPTY +'</table>'
DECLARE @RESULT_COST_LOC__AND_DISTRICT_NOT_MATCHING_HEADER VARCHAR(400)
SET @RESULT_COST_LOC__AND_DISTRICT_NOT_MATCHING_HEADER='<H3>PositionNumber where Department and Cost Center are Not Macthing.</H3>
<table border = 1>
<tr>
<th> POS_ID </th> <th> POS_NUM </th> <th> DEPARTMENT </th> <th> DISTRICT </th> <th> COST_LOC </th></tr>'
SET @RESULT_COST_LOC__AND_DISTRICT_NOT_MATCHING = CAST(( SELECT [POS_ID] AS 'td','',RTRIM([POS_NUM]) AS 'td','',
ISNULL(LEFT(DEPARTMENT,LEN(DEPARTMENT)-1),' ') AS 'td','', ISNULL([DISTRICT],' ') AS 'td','',ISNULL([COST_LOC],' ') AS 'td'
FROM @RECORDS_THAT_NEED_TO_SEND_EMAIL
WHERE
(RTRIM(LTRIM(LEFT(DEPARTMENT,LEN(DEPARTMENT)-1))) <> RTRIM(LTRIM(COST_LOC)))
FOR XML PATH('tr'), ELEMENTS ) AS VARCHAR(MAX))
SET @BODY = @BODY + @RESULT_COST_LOC__AND_DISTRICT_NOT_MATCHING_HEADER+ @RESULT_COST_LOC__AND_DISTRICT_NOT_MATCHING +'</table>'
SET @BODY = @BODY + @HTMLFOOTER
USE DDDADMINISTRATION_DB
--SEND EMAIL
exec DDDADMINISTRATION_DB.dbo.uspSMTP_NOTIFY_HTML
@EmailSubject="District,Department & CostCenter Discrepancies",
@EmailMessage = @BODY,
@ToEmailAddress="[email protected]",
@FromEmailAddress="[email protected]"
EXEC msdb.dbo.sp_send_dbmail
@profile_name="MY POROFILE", -- replace with your SQL Database Mail Profile
@body = @BODY,
@body_format="HTML",
@recipients="[email protected]", -- replace with your email address
@subject="District,Department & CostCenter Discrepancies" ;
.
7585900cookie-checkKonvertieren Sie eine SQL-Abfrageergebnistabelle in eine HTML-Tabelle für E-Mailyes
Vielleicht möchten Sie diese Antwort sehen. Ein
XQuer FLWOR
basierter Ansatz, der SELECT als Parameter mit Unterstützung für Header, CSS und Hyperlinks verwendet.– Schnugo
9. Februar 18 um 22:08 Uhr