【问题标题】:Convert a SQL query result table to an HTML table for email将 SQL 查询结果表转换为电子邮件的 HTML 表
【发布时间】:2011-10-27 13:21:18
【问题描述】:

我正在运行一个返回结果表的 SQL 查询。我想使用 dbo.sp_send_dbMail 通过电子邮件发送表格。

在 SQL 中是否有一种直接的方法可以将表格转换为 HTML 表格?目前,我正在使用 COALESCE 手动构建它,并将结果放入我用作 emailBody 的 varchar 中。

有没有更好的方法来做到这一点?

【问题讨论】:

  • 您可能想查看this answer。一种基于 XQuer FLWOR 的方法,将 SELECT 作为参数,支持标题、CSS 和超链接。

标签: html sql sp-send-dbmail


【解决方案1】:

我制作了一个动态过程,它将任何随机查询转换为 HTML 表,因此您不必像其他响应中那样对列进行硬编码。

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

用法:

DECLARE @html nvarchar(MAX);
EXEC spQueryToHtmlTable @html = @html OUTPUT,  @query = N'SELECT * FROM dbo.People', @orderBy = N'ORDER BY FirstName';

EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'Foo',
    @recipients = 'bar@baz.com;',
    @subject = 'HTML email',
    @body = @html,
    @body_format = 'HTML',
    @query_no_truncate = 1,
    @attach_query_result_as_file = 0;

相关:这是将任意查询转换为CSV string 的类似代码。

【讨论】:

  • 这是我一直在寻找并计划写的东西。这是您通常看到的更灵活的版本。我编辑以修复 RealQuery 的行实际上是一行,如果有人复制和粘贴将无法按原样工作。好代码,非常好的代码。
  • 这不会为NULL 值生成单元格。要解决此问题,请将 ELEMENTS) 更改为 ELEMENTS XSINIL)。然后,空单元格将具有属性xsi:nil="true",可能会被特殊样式替换。很棒的程序!
  • 太棒了!此外,如果要运行存储过程,可以将存储过程结果插入到临时表中,然后从中选择
  • 优秀的SP,这应该是最好的答案!
  • 也许我只是愚蠢,但即使在阅读说明后我也不明白如何实现这个答案。他们对我没有任何意义。这是鲁比吗?我只是将@query 绑定到查询字符串并运行文件吗?有一些对白痴友好的说明会很好。
【解决方案2】:

这是标题为“Format query output into an HTML table - the easy way [archive]”的文章中的一种方法。您需要用您自己的查询的详细信息替换本示例中的查询,该示例获取表列表和行数。

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, '&lt;', '<' ), '&gt;', '>' )
          + '</table>'

print @body

一旦您拥有@body,您就可以使用任何您想要的电子邮件机制。

【讨论】:

  • 在字符串基础上创建XML 总是非常危险的...您正在替换&lt;&gt;,但是还有更多危险的字符,例如&amp;。您可以检查 [this answer] 以了解另一种方法。
  • 我喜欢这种方法。您是否能够添加将类添加到某些行的功能(即每隔一行有另一个 CSS 类)?我知道我可以将您的解决方案抽象为使用动态 SQL,尽管这有悖于目的。
  • @Eli,无论如何都不会被认为比现在更“hacky”。​​
【解决方案3】:

这可能会给你一些想法——

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 = 'bruhaspathy@hotmail.com', -- replace with your email address
@subject = 'E-mail in Tabular Format' ;


DROP TABLE #Temp

【讨论】:

  • 有趣的方法,虽然我不明白为什么每个字段后面都有额外的空白字符串;你能解释一下那件作品的功能/需求吗?因此,我需要的另一部分无法使用您的解决方案,即向某些行添加一个类(即每隔一行有另一个 CSS 类)。
  • @Eli 我猜 MS SQL XML 将类似的命名字段组合为一个。没有名称的空格或空值不会序列化,将td 值分隔到它们自己的列中,而不是串联。
【解决方案4】:

下面是我常用的脚本。我用它来在两个表/视图上运行带有 SQL 作业的脚本,并通过邮件将结果作为两个 HTML 表发送。当然你应该在运行之前创建邮件配置文件。

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= '<mailaddress@domain.com>',
    @from_address = @mailfrom,
    @reply_to = '<mailaddress@domain.com>',
    @subject = @subject,
    @body = @mailbody,
    @body_format = 'HTML'
--   ,@importance = 'HIGH'
END

【讨论】:

    【解决方案5】:

    基于 JustinStolle 代码(谢谢),我想要一个无需指定列名的通用解决方案。

    本示例使用的是临时表的数据,当然可以根据需要进行调整。

    这是我得到的:

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

    【讨论】:

      【解决方案6】:

      假设有人在这里找到了他的方式并且不理解标记答案 SQL 的用法,请阅读我的...它已编辑并且有效。表:staff,列:staffname、staffphone 和 staffDOB

      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, '&lt;', '<' ), '&gt;', '>' )
                    + '<table>'
      print @body
      

      【讨论】:

        【解决方案7】:

        我尝试使用上面的 Mahesh 示例打印多个表。为他人方便而发帖

        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  = 'Slohani@azdes.gov', 
                                      @FromEmailAddress  = 'Slohani@azdes.gov' 
        
        
        EXEC msdb.dbo.sp_send_dbmail
        @profile_name = 'MY POROFILE', -- replace with your SQL Database Mail Profile 
        @body = @BODY,
        @body_format ='HTML',
        @recipients = 'Recepients@internalmail.com', -- replace with your email address
        @subject = 'District,Department & CostCenter Discrepancies' ;
        

        【讨论】:

          【解决方案8】:

          所有其他答案都使用变量和 SET 操作。这是在 select 语句中执行此操作的一种方法。只需将其作为一列放入现有选择中即可。

              (SELECT 
                  '<table style=''font-family:"Verdana"; font-size: 10pt''>'
                              + '<tr bgcolor="#9DBED4"><th>col1</th><th>col2</th><th>col3</th><th>col4</th><th>col5</th></tr>'
                              + replace( replace( body, '&lt;', '<' ), '&gt;', '>' )
                              + '</table>'
              FROM
              (
                  select cast( (
                      select td = cast(col1 as varchar(5)) + '</td><td align="right">' + col2 + '</td><td>' + col3 + '</td><td align="right">' + cast(col4 as varchar(5)) + '</td><td align="right">' + cast(col5 as varchar(5)) + '</td>'
                      from (
                              select col1  = col1,
                                      col2 = col2,
                                      col3 = col3,
                                      col4 = col4,
                                      col5 = col5
                              from m_LineLevel as onml
                              where onml.pkey = oni.pkey
                              ) as d
                      for xml path( 'tr' ), type ) as varchar(max) ) as body
              ) as bodycte) as LineTable
          

          【讨论】:

            【解决方案9】:

            按照一段代码,我已经准备好为文档生成 HTML 文件,其中包括每个表中的表名和用途以及表元数据信息。可能会有帮助!

            use Your_Database_Name;
            print '<!DOCTYPE html>'
            PRINT '<html><body>'
            SET NOCOUNT ON
            DECLARE @tableName VARCHAR(30)
            DECLARE tableCursor CURSOR LOCAL FAST_FORWARD FOR
                SELECT T.name AS TableName 
                  FROM sys.objects AS T
                 WHERE T.type_desc = 'USER_TABLE'
                 ORDER BY T.name
            OPEN tableCursor
            FETCH NEXT FROM tableCursor INTO @tableName
            WHILE @@FETCH_STATUS = 0 BEGIN
                print '<table>'
                print '<tr><td><b>Table Name: <b></td><td>'+@tableName+'</td></tr>'
                print '<tr><td><b>Prupose: <b></td><td>????YOu can Fill later????</td></tr>'
                print '</table>'
            
                print '<table>'
                print '<tr><th>ColumnName</th><th>DataType</th><th>Size</th><th>PrecScale</th><th>Nullable</th><th>Default</th><th>Identity</th><th>Remarks</th></tr>'
                SELECT  concat('<tr><td>',
                        LEFT(C.name, 30) /*AS ColumnName*/,'</td><td>',
                       LEFT(ISC.DATA_TYPE, 10) /*AS DataType*/,'</td><td>',
                       C.max_length /*AS Size*/,'</td><td>',
                       CAST(P.precision AS VARCHAR(4)) + '/' + CAST(P.scale AS VARCHAR(4)) /*AS PrecScale*/,'</td><td>',
                       CASE WHEN C.is_nullable = 1 THEN 'Null' ELSE 'No Null' END /*AS [Nullable]*/,'</td><td>',
                       LEFT(ISNULL(ISC.COLUMN_DEFAULT, ' '), 5)  /*AS [Default]*/,'</td><td>',
                       CASE WHEN C.is_identity = 1 THEN 'Identity' ELSE '' END /*AS [Identity]*/,'</td><td></td></tr>')
                FROM   sys.objects AS T
                       JOIN sys.columns AS C ON T.object_id = C.object_id
                       JOIN sys.types AS P ON C.system_type_id = P.system_type_id and c.user_type_id = p.user_type_id
                       JOIN INFORMATION_SCHEMA.COLUMNS AS ISC ON T.name = ISC.TABLE_NAME AND C.name = ISC.COLUMN_NAME
                WHERE  T.type_desc = 'USER_TABLE'
                  AND  T.name = @tableName
                ORDER BY T.name, ISC.ORDINAL_POSITION
                print '</table>'
                print '</br>'
                FETCH NEXT FROM tableCursor INTO @tableName
            
            END
            
            CLOSE tableCursor
            DEALLOCATE tableCursor
            SET NOCOUNT OFF
            PRINT '</body></html>'
            

            【讨论】:

              【解决方案10】:

              JustinStolle 以不同的方式回答。几点说明:

              • print 语句可能会将字符串截断为 4000 个字符,但例如我的测试字符串长度为 9520 个字符。
              • [tr/th] 表示层次结构,例如&lt;tr&gt;&lt;th&gt;...&lt;/th&gt;&lt;/tr&gt;
              • [@name] 将字段添加为 XML 属性。
              • MS SQL XML 连接同名字段,因此字段之间的null 可以防止这种情况发生。

              declare @body nvarchar(max)
              
              select @body = cast((
                  select N'2' [@cellpadding], N'2' [@cellspacing], N'1' [@border],
                         N'Database Table' [tr/th], null [tr/td],
                         N'Entity Count' [tr/th], null [tr/td],
                         N'Total Rows' [tr/th], null,
                         (select  object_name( object_id ) [td], null,
                                  count( distinct name ) [td], null,
                                  count( * )  [td], null
                           from sys.columns
                           group by object_name( object_id )
                           for xml path('tr'), type)
                      for xml path('table'), type
                      ) as nvarchar(max))
              
              print @body  -- only shows up to 4000 characters depending
              

              【讨论】:

                【解决方案11】:

                这是我的实现:对 html 表的任何查询结果。

                我正在创建一些辅助程序来实现这一点。这些帮助程序很灵活,可以在各种情况下重复使用。

                • fnValidateDynamicSql - 验证通过的动态语句
                • spAlterTblByRs - 将任何 SQL 语句结果保存到 #table。允许从代码中完全删除动态 SQL
                • spQueryResultAsHtmlTable - 从任何传递的 SQL 语句创建 html 表

                享受:)

                CREATE FUNCTION [dbo].[fnValidateDynamicSql]
                    (@Sql NVARCHAR(MAX),     /* dynamic sql statement */
                     @Params NVARCHAR(MAX)   /* parameters, if dynamic SQL is parametrized. Pass NULL if there are no params */
                    )
                RETURNS NVARCHAR(MAX)
                AS
                    /*  Check or @Sql statement is valid
                     *  Returns NULL if valid, exception message otherwise   
                     */
                BEGIN
                    DECLARE @Result VARCHAR(1000);
                
                    IF EXISTS (SELECT NULL
                               FROM [sys].[dm_exec_describe_first_result_set](@Sql, @Params, 0)
                               WHERE [error_message] IS NOT NULL
                                 AND [error_number] IS NOT NULL
                                 AND [error_severity] IS NOT NULL
                                 AND [error_state] IS NOT NULL
                                 AND [error_type] IS NOT NULL
                                 AND [error_type_desc] IS NOT NULL)
                    BEGIN
                        SELECT @Result = [error_message]
                        FROM [sys].[dm_exec_describe_first_result_set](@Sql, @Params, 0)
                        WHERE [column_ordinal] = 0;
                    END;
                    
                    IF NULLIF(LTRIM(RTRIM(@Sql)), '') IS NULL 
                       SET @Result = '@Sql is NULL';
                
                    RETURN @Result;
                END;
                GO
                
                CREATE PROCEDURE [dbo].[spAlterTblByRs]
                    @ErrCode  INT OUT,
                    @ErrMsg   VARCHAR(4000) OUT,    
                    @Sql      NVARCHAR(MAX),         /* Query stmt  */
                    @Params   NVARCHAR(MAX) = NULL,  /* Query parameters (like in sp_executesql) */
                    @Tbl      NVARCHAR(256),         /* Table name */
                    @DummyCol NVARCHAR(256),         /* Dummy column name (will be removed) */
                    @PopulateTable BIT = NULL        /* If 1, then populate altered table by @Sql query data */ 
                AS
                    /* Alters table by recordset to be used. Populates data, if required. */    
                BEGIN
                    SET NOCOUNT ON;
                    SET ARITHABORT ON;
                
                    BEGIN TRY
                
                        DECLARE @ERR_CODE_OK        INT =   0
                            ,   @ERR_CODE_FAILURE   INT =   50000;
                
                        SET @ErrCode = @ERR_CODE_OK;
                
                        
                        IF NULLIF(LTRIM(RTRIM(@Tbl)), '') IS NULL THROW @ERR_CODE_FAILURE, '@Tbl is empty', 1;
                        IF NULLIF(LTRIM(RTRIM(@DummyCol)), '') IS NULL THROW @ERR_CODE_FAILURE, '@DummyCol is empty', 1;
                
                
                        IF [dbo].[fnValidateDynamicSql](@Sql, @Params) IS NOT NULL
                        BEGIN
                            SET @ErrMsg = 'Invalid @Sql received: ' + [dbo].[fnValidateDynamicSql](@Sql, @Params);
                            ;THROW @ERR_CODE_FAILURE, @ErrMsg, 1;
                        END;
                
                
                        DECLARE @AlterStmt      NVARCHAR(MAX) = SPACE(0);
                        DECLARE @RemColStmt     NVARCHAR(MAX) = SPACE(0);   
                             
                        --  prepare existing table alter Stmt by previuos rs structure
                        SET @AlterStmt = 'ALTER TABLE ' + @tbl + ' ADD ' + CHAR(13);
                
                        ;WITH [rsStructure] AS (
                            SELECT
                                    [name]
                                ,   [system_type_name]
                                ,   [is_nullable]            
                            FROM [sys].[dm_exec_describe_first_result_set](
                                            @Sql
                                        ,   @Params
                                        ,   0
                            )       
                        )
                        SELECT 
                             @AlterStmt += QUOTENAME([name]) + SPACE(1) + [system_type_name] + IIF([is_nullable] = 0, ' NOT NULL' , SPACE(0)) + ',' + CHAR(13)
                        FROM [rsStructure];
                
                        SET @AlterStmt = LEFT(@AlterStmt, LEN(@AlterStmt) - 2);
                
                        --  finally update table structure
                        EXEC [sys].[sp_executesql] @AlterStmt; 
                        
                
                        --  remove dummy column
                        SET @RemColStmt = 'ALTER TABLE ' + @tbl + ' DROP COLUMN ' + @DummyCol;
                        EXEC [sys].[sp_executesql] @RemColStmt; 
                
                        --  populate table with @Sql statement data
                        IF @PopulateTable = 1
                        BEGIN
                            EXEC('INSERT INTO ' + @tbl + ' ' + @sql);
                        END;
                        
                        
                    END TRY
                    BEGIN CATCH
                    
                        /* Use some error formatting sp instead */
                        SELECT  @ErrCode    =   ERROR_NUMBER()
                            ,   @ErrMsg     =   ERROR_MESSAGE();
                
                    END CATCH
                
                    RETURN @ErrCode;
                
                END
                
                
                GO
                
                GO
                CREATE PROCEDURE [dbo].[spQueryResultAsHtmlTable]
                    @ErrCode    INT             OUT
                ,   @ErrMsg     NVARCHAR(4000)  OUT
                ,   @Sql        NVARCHAR(MAX)
                ,   @Params     NVARCHAR(MAX)
                ,   @HtmlTable  NVARCHAR(MAX)   OUT
                AS
                /*  Makes Html table by result, returned by provided @Query 
                 */
                BEGIN
                    
                    SET NOCOUNT ON;
                    SET ARITHABORT ON;
                        
                    BEGIN TRY
                
                        DECLARE @ERR_CODE_OK        INT =   0
                            ,   @ERR_CODE_FAILED    INT =   50000;          
                
                        SET @ErrCode = @ERR_CODE_OK;
                
                        DECLARE @HtmlAsHml  XML
                            ,   @ColumnList NVARCHAR(MAX)   =   SPACE(0);       
                
                        IF NULLIF(LTRIM(RTRIM(@Sql)), SPACE(0)) IS NULL THROW @ERR_CODE_FAILED, 'Empty @Query received', 1;
                                    
                                
                        IF OBJECT_ID('tempdb..#QueryResult') IS NOT NULL DROP TABLE [#QueryResult];
                        CREATE TABLE [#QueryResult] ([dummy_col] BIT);
                
                        EXEC [dbo].[spAlterTblByRs]
                            @ErrCode        =   @ErrCode        OUT
                        ,   @ErrMsg         =   @ErrMsg         OUT
                        ,   @Sql            =   @Sql            
                        ,   @Params         =   @Params         
                        ,   @Tbl            =   '#QueryResult'  
                        ,   @DummyCol       =   'dummy_col'     
                        ,   @PopulateTable  =   1;
                
                        IF @ErrCode <> 0 THROW @ErrCode, @ErrMsg, 1;
                                    
                            
                        SELECT @ColumnList += IIF([column_ordinal] = 1, SPACE(0), ',') + '[td] = [' + [name] + ']'
                        FROM [sys].[dm_exec_describe_first_result_set](
                            @Sql    /*  @tsql                       */
                        ,   @Params /*  @params                     */
                        ,   0       /*  @browse_information_mode    */      
                        )       
                        ORDER BY [column_ordinal] ASC;
                
                
                        DECLARE @h  XML
                        ,       @d  XML;    
                
                        /* Prepare headers */
                        ;WITH [headers] AS (
                            SELECT [h] = CONVERT(XML, (SELECT 
                                [th] = [name] 
                            FROM [sys].[dm_exec_describe_first_result_set](
                                @Sql    /*  @tsql                       */
                            ,   @Params /*  @params                     */
                            ,   0       /*  @browse_information_mode    */      
                            )   
                            ORDER BY [column_ordinal] ASC
                            FOR XML PATH(''), ROOT('tr')))
                        )
                        SELECT @h = [h] FROM [headers];
                            
                        
                        /* Prepare rows */
                        SET @sql = N'
                        ;WITH [data] AS (
                            SELECT [d] = (SELECT    
                                ' + @ColumnList + '
                            FROM [#QueryResult] 
                            FOR XML RAW (''tr''), ELEMENTS XSINIL, TYPE)
                        )       
                        SELECT @d = [d] FROM [data]';
                
                        SET @params = N'@d xml output';
                
                        EXECUTE [sp_executesql] 
                            @stmt   =   @sql
                        ,   @params =   @params     
                        ,   @d      =   @d      OUTPUT;
                        
                
                        /* Make table html */
                        SET  @HtmlAsHml = CONVERT(XML, (SELECT [*] = @h, [*] = @d FOR XML PATH('table')));
                
                        SET @HtmlAsHml.modify('insert attribute cellpadding {"2"} into (table)[1]')
                        SET @HtmlAsHml.modify('insert attribute cellspacing {"2"} into (table)[1]')
                        SET @HtmlAsHml.modify('insert attribute border {"1"} into (table)[1]')
                    
                        
                        /* Prepare value to be returned */
                        SET @HtmlTable = CONVERT(NVARCHAR(MAX), @HtmlAsHml);
                        
                               
                    END TRY
                    BEGIN CATCH             
                
                        /* Use some error formatting sp instead */
                        SELECT  @ErrCode    =   ERROR_NUMBER()
                            ,   @ErrMsg     =   ERROR_MESSAGE();
                
                    END CATCH;
                    
                    RETURN @ErrCode;
                    
                END;
                GO
                
                /* Usage */
                
                DECLARE
                    @ErrCode    INT             
                ,   @ErrMsg     NVARCHAR(4000)  
                ,   @Sql        NVARCHAR(MAX)   =   'select top (10) * from sys.tables'
                ,   @HtmlTable  NVARCHAR(MAX);
                
                EXEC [dbo].[spQueryResultAsHtmlTable]
                    @ErrCode    =   @ErrCode    OUT
                ,   @ErrMsg     =   @ErrMsg     OUT
                ,   @Sql        =   @Sql        
                ,   @Params     =   NULL        
                ,   @HtmlTable  =   @HtmlTable  OUT; /* YOur desired html table here */
                
                IF @ErrCode <> 0 THROW @ErrCode, @ErrMsg, 1;
                

                【讨论】:

                  猜你喜欢
                  • 2012-02-16
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-12-21
                  • 2011-07-08
                  • 1970-01-01
                  • 2015-02-20
                  • 2020-10-31
                  • 1970-01-01
                  相关资源
                  最近更新 更多