T-SQL 语句打印包含对齐数据的完整表

T-SQL statement to Print full table with aligned data

提问人:Star Galaxy 提问时间:10/25/2023 最后编辑:Star Galaxy 更新时间:11/7/2023 访问量:76

问:

对于上下文,我正在尝试创建一封电子邮件,用户可以在其中以表格形式查看查询的数据。我希望数据(来自每列)在每行(因此,一个表)的相同位置对齐。每列中的数据长度可以不同。现在,我不专注于发送电子邮件。我专注于简化文本对齐和/或表格查询。如果您想添加有关发送数据库电子邮件的信息,请随意;我还没有转到那部分。

编辑:我正在使用循环,因为每个解决方案都分配了一个用户。每个解决方案可以有不同的用户。项目目标是向每个用户发送一封电子邮件,其中仅列出该用户的解决方案。(对于可以改变什么,我的发言权有限。电子邮件是基于到期日期的时间触发事件。

打印时,我将其格式化为打印为:

Solution: 1234-12-1  | Status ID: 1  | Expires: 01-01-2024  
Solution: 1234-12-2  | Status ID: 12 | Expires: 01-01-2024 
Solution: 56789-1    | Status ID: 1  | Expires: 01-01-2024 
Solution: 56789-2-22 | Status ID: 1  | Expires: 01-01-2024 
Solution: 987-654, a | Status ID: 12 | Expires: 01-01-2024 

我的基本目标是:(越好越好,但它有效)。

Solution    | Status ID | Expiration
'-----------------------------------   
1234-12-1   |  1a       | 1-1-2024 
1234-12-2   |  12b      | 01-01-2024 
56789-1     |  1a       | 01-01-2024 
56789-2-22  |  1a       | 01-01-2024 
987-654, a  |  12b      | 01-01-2024

我正在使用 Microsoft SSMS 2019、SQL Server 2019 和 Transact-SQL。这是我当前的代码,被精简到最低限度。我当前的代码工作正常,但没有达到我想要的目标。(它目前尚未设置为发送电子邮件,但我想在进行下一步之前获得正确的打印/显示格式。

DECLARE @_MaxTemp INT;
DECLARE @_Current INT = 1;
DECLARE @_SolutionName nvarchar(30);
DECLARE @_ExpirationDate nvarchar(20);

DECLARE @_StatusID nvarchar(5);

SELECT 
    IDENTITY(int,1,1) as TempID
    ,s.[Name] as SolutionName
    ,s.[StatusID] as StatusID
    ,n.[ExpirationDate] as ExpirationDate
  INTO #temp
  FROM [dbo].[Solution] AS s
  INNER JOIN [dbo].[Notifications] AS n
    ON s.[ID] = n.SolutionID

SET @_MaxTemp = (SELECT COUNT(TempID) FROM #Temp)

WHILE @_Current < @_MaxTemp
  BEGIN
    SET @_SolutionName = (SELECT SolutionName FROM #temp WHERE TempID = @_Current);
    SET @_StatusID = (SELECT StatusID FROM #temp WHERE TempID = @_Current)
    SET @_ExpirationDate = CAST((SELECT ExpirationDate FROM #TEMP WHERE TempID = @_Current) AS nvarchar(25));
    SET @_

    PRINT FORMATMESSAGE('Solution: %s  | Status ID: %s  | Expires: 01-01-2024', @_SolutionName, @_StatusID, @_ExpirationDate);

    SET @_Current += 1;
  END

DROP TABLE IF EXISTS  #temp`

我没有循环,而是在下面尝试了一些简单的东西。毫不奇怪,它没有奏效。(“在这种情况下,不允许使用子查询。只允许使用标量表达式。

print (Select * from #temp)

我意识到我可以查询字符数,设置所需的长度,对其进行数学运算,并添加所需的空格来对齐它。但是,我需要对每个单元格重复此操作(将提取更多列,但示例不需要)。作为初学者,我的代码将变成意大利面条代码。

我尝试查看其他 stackoverflow 帖子,但它通常是关于拉取特定单元格/行、查询表描述或问题标题与问题上下文不匹配。它有 10 年的历史中的一些是改进/新方法尚不存在的,这无济于事。当我搜索 Microsoft 文档时,它是关于一般的打印语句、提取特定单元格/行或表格列表(而不是内容)。在一般搜索中,我遇到了类似的搜索问题。

sql-server t-sql 字符串格式设置 文本对齐方式

评论

3赞 siggemannen 10/25/2023
试图让打印看起来不错是浪费时间,因为您无法捕获输出以将其发送到其他地方。您应该从结果中创建一个 html“表”并通过电子邮件发送。FOR XML 非常适合生成带有 trs 等的表
0赞 Sean Lange 10/25/2023
我完全同意@siggemannen。如果要格式化输出,请使用 html。你当然不需要循环来做到这一点。
2赞 Joel Coehoorn 10/25/2023
发送 html 电子邮件并实际使用表格标签。并且不要将输出呈现为 SQL 的一部分。从客户端语言运行它,并从那里发送电子邮件。
0赞 Star Galaxy 10/25/2023
耶!那太有道理了!我几乎没有使用过 SQL Server,所以我仍在学习它的功能和不足。我的经理只是说先把它打印出来,然后推迟电子邮件;然而,她并不是最精通技术的人,但至少了解我们在做什么。谢谢!
0赞 Yitzhak Khabinsky 10/25/2023
在这里看看我的答案:stackoverflow.com/questions/69036797/......

答:

1赞 Star Galaxy 10/26/2023 #1

根据我收到的多条评论,避免使用 SQL“打印”来创建干净的格式化表。若要在 SSMS 外部向用户显示数据,请使用 HTML 和/或 XML 将数据格式化为所需的布局。

经过搜索,我发现一些网站提供了使用 SQL Server 生成 HTML/XML 的指导。

在评论中,Yitzhak Khabinsky 指出了另一个 StackOverflow 链接,该链接介绍了如何使用 XML 格式化 SQL 结果:

为了在 SQL Server 中发送电子邮件,Microsoft 提供了一些涵盖 SQL 数据库电子邮件的培训/文档:

编辑:我现在已经重构了代码。由于隐私原因,它已被简化,但仍然提供了一个答案。如建议,现在使用 HTML 格式和光标/获取。但是,由于管理决策,仍使用 SQL dbmail (SSMS)。

    DECLARE @Solution nvarchar(10);
    DECLARE @StatusID int;
    DECLARE @ExpirationDate date;
    DECLARE @HTML_file nvarchar(2000);
    
    CREATE TABLE #temp (Solution nvarchar(10), StatusID int, ExpirationDate Date)
        
    DECLARE message_cursor CURSOR LOCAL SCROLL FOR 
        SELECT 
            [Solution], 
            [StatusID], 
            [ExpirationDate] 
        FROM dbo.TableName      
    
    OPEN message_cursor
    FETCH NEXT FROM message_cursor INTO @Solution, @StatusID, @ExpirationDate
    
    WHILE @@FETCH_STATUS = 0
    BEGIN   
    IF @StatusID = 1 --solution in-use
    BEGIN
        INSERT INTO #temp
        VALUES
            (@Solution, 
            @StatusID, 
            @ExpirationDate)
    END
        
    If @StatusID = 5 --solution disposed
    BEGIN
    --create HTML file to send out
        SET @HTML_file = CONCAT(
        N'<body>
          <table style="border-collapse: collapse;border: 1px solid gray;">
            <tr>
              <th>Solution</th>
              <th>Project #</th>
              <th>Expiration Date</th>
            </tr>' 
        ,CAST ((SELECT 
            td = Solution, ''
            ,td = StatusID, ''
            ,td = ExpirationDate, ''
            FROM #temp FOR XML PATH('tr'), type) AS nvarchar(MAX))
        ,'</table></body>')
    
    --executes email to user
     EXEC msdb.dbo.sp_send_dbmail
        @profile_Name = 'profilename'
        ,@recipients = '[email protected]'
        ,@subject = 'Put your email title here'
        ,@body = @HTML_file
        ,@body_format = 'HTML';
    DELETE FROM #temp
    END

    FETCH NEXT FROM email_cursor INTO @Solution, @StatusID, 
    @ExpirationDate
    END
    
    DROP TABLE #temp
    CLOSE email_cursor
    DEALLOCATE email_cursor