SSMS 输出中的格式列长度

Joe*_*yes 4 sql-server ssms t-sql sql-server-2012 reporting

SQL Server 2012。本文底部的示例查询。

我正在尝试为上次备份给定数据库的时间创建一个简单的报告。

在 SSMS 中执行输出到文本的示例查询时,DB_NAME列被格式化为数据的最大可能大小(DB2 中存在相同的问题,顺便说一句)。所以,我有一列包含从不超过 12 个字符的数据,但它存储在 中varchar(128),无论如何我都会得到 128 个字符的数据。RTRIM对输出没有影响。

您知道有没有一种优雅的方法可以使格式化的列长度成为实际数据的最大大小,而不是数据的最大潜在大小?

我想存在一个xp_sprintf()函数,但我不熟悉它,而且它看起来不是很健壮。

我试过像这样铸造它:

DECLARE @Servername_Length int;
SELECT  @Servername_Length =        LEN(    CAST(   SERVERPROPERTY('Servername') AS VARCHAR(MAX)    )   ) ;

...
SELECT  
   CONVERT(CHAR(@Servername_Length), SERVERPROPERTY('Servername')) AS Server, 
...
Run Code Online (Sandbox Code Playgroud)

但是 SQL Server 不会让我在转换时@database_name_Length在我的varchar定义中使用该变量。显然,SQL Server 在声明charorvarchar变量时需要一个文字数字。

我开始在字符串中构建语句并使用类似的东西sp_executesql,或者构建一个具有我需要的实际列长度的临时表,这两者都比我希望去的只是为了不获得 100 更麻烦我的输出中有 128 个字符的列中的空格。

搜索了互联网并找到了bupkus。

也许我在寻找错误的东西,或者谷歌对我不利。

似乎 SSMS 会将列格式化为允许的最大大小,即使实际数据要小得多。我希望有一种优雅的方式来“解决”这个问题,而无需跳过箍。我正在使用 SSMS 2012。

如果我转到结果到网格,然后转到 Excel 或类似的东西,则会消除尾随空格。不过,我希望基本上创建一份我通过电子邮件发送的报告。

示例查询

--------------------------------------------------------------------------
QUERY:
--------------------------------------------------------------------------
SELECT  
   CONVERT(CHAR(32), SERVERPROPERTY('Servername')) AS Server, 
   '''' + msdb.dbo.backupset.database_name + '''',  
   MAX(msdb.dbo.backupset.backup_finish_date) AS last_db_backup_date 
FROM   msdb.dbo.backupmediafamily  
   INNER JOIN msdb.dbo.backupset ON msdb.dbo.backupmediafamily.media_set_id = msdb.dbo.backupset.media_set_id  
WHERE  msdb..backupset.type = 'D' 
GROUP BY 
   msdb.dbo.backupset.database_name  
ORDER BY  
   msdb.dbo.backupset.database_name 
Run Code Online (Sandbox Code Playgroud)

Han*_*non 6

CONVERT(VARCHAR(xx), ColumnName)如果您希望列在文本输出视图中显示得更短,则需要在所有列上使用。

将您的查询转换为以下内容:

SELECT [Server] = CONVERT(VARCHAR(30), SERVERPROPERTY('Servername')) 
   , DatabaseName = CONVERT(VARCHAR(30), '''' + bs.database_name + '''')
   , LastDatabaseBackupDate = CONVERT(VARCHAR(30), MAX(bs.backup_finish_date))
FROM msdb.dbo.backupmediafamily  bmf
    INNER JOIN msdb.dbo.backupset bs ON bmf.media_set_id = bs.media_set_id  
WHERE  bs.[type] = 'D' 
GROUP BY bs.database_name  
ORDER BY bs.database_name;
Run Code Online (Sandbox Code Playgroud)

这将提供类似于以下内容的输出:

Server                         DatabaseName                   LastDatabaseBackupDate
------------------------------ ------------------------------ ------------------------------
[ServerName]                   'A'                            Sep 25 2015 11:32AM
[ServerName]                   'B'                            Apr 21 2015 12:09PM
[ServerName]                   'C'                            Feb 24 2015  9:16PM
[ServerName]                   'D'                            Oct  8 2014 11:02AM
[ServerName]                   'E'                            May 14 2014  6:27PM

(5 row(s) affected)
Run Code Online (Sandbox Code Playgroud)

如果您希望能够在不修改 T-SQL 代码的情况下动态更改列宽,则需要使用动态 SQL:

DECLARE @ColumnWidth VARCHAR(4);
DECLARE @Cmd NVARCHAR(MAX);

SET @ColumnWidth = '24';
SET @Cmd = '
SELECT [Server] = CONVERT(VARCHAR(' + @ColumnWidth + '), SERVERPROPERTY(''Servername'')) 
   , DatabaseName = CONVERT(VARCHAR(' + @ColumnWidth + '), '''''''' + bs.database_name + '''''''')
   , LastDatabaseBackupDate = CONVERT(VARCHAR(' + @ColumnWidth + '), MAX(bs.backup_finish_date))
FROM msdb.dbo.backupmediafamily  bmf
    INNER JOIN msdb.dbo.backupset bs ON bmf.media_set_id = bs.media_set_id  
WHERE  bs.[type] = ''D'' 
GROUP BY bs.database_name  
ORDER BY bs.database_name;
';

EXEC (@cmd);
Run Code Online (Sandbox Code Playgroud)

在这里,我已将所有列的宽度设置为 24,结果如下所示:

Server                   DatabaseName             LastDatabaseBackupDate
------------------------ ------------------------ ------------------------
SERVERNAME               'A'                      Sep 25 2015 11:32AM
SERVERNAME               'A'                      Apr 21 2015 12:09PM
SERVERNAME               'A'                      Feb 24 2015  9:16PM
SERVERNAME               'A'                      Oct  8 2014 11:02AM
SERVERNAME               'A'                      May 14 2014  6:27PM

(5 row(s) affected)
Run Code Online (Sandbox Code Playgroud)

如果您真的想发疯并让列自动调整大小,您可以这样做:

DECLARE @ColumnWidthServer VARCHAR(4);
DECLARE @ColumnWidthDatabase VARCHAR(4);
DECLARE @ColumnWidthLastBackup VARCHAR(4);

DECLARE @Cmd NVARCHAR(MAX);

SELECT @ColumnWidthServer = 1 + LEN(CONVERT(VARCHAR(128), SERVERPROPERTY('Servername')))
   , @ColumnWidthDatabase = 1 + MAX(LEN('''' + bs.database_name + ''''))
   , @ColumnWidthLastBackup = 1 + MAX(LEN(CONVERT(VARCHAR(128), bs.backup_finish_date)))
FROM msdb.dbo.backupmediafamily  bmf
    INNER JOIN msdb.dbo.backupset bs ON bmf.media_set_id = bs.media_set_id  
WHERE  bs.[type] = 'D';

SET @Cmd = '
SELECT [Server] = CONVERT(VARCHAR(' + @ColumnWidthServer + '), SERVERPROPERTY(''Servername'')) 
   , DatabaseName = CONVERT(VARCHAR(' + @ColumnWidthDatabase + '), '''''''' + bs.database_name + '''''''')
   , LastDatabaseBackupDate = CONVERT(VARCHAR(' + @ColumnWidthLastBackup + '), MAX(bs.backup_finish_date))
FROM msdb.dbo.backupmediafamily  bmf
    INNER JOIN msdb.dbo.backupset bs ON bmf.media_set_id = bs.media_set_id  
WHERE  bs.[type] = ''D'' 
GROUP BY bs.database_name  
ORDER BY bs.database_name;
';

EXEC (@cmd);
Run Code Online (Sandbox Code Playgroud)


Sol*_*zky 5

如果您想要快速简便的操作,并且可以接受所有列宽相同,请尝试SQLCMD.exe-Y选项:

C:\>SQLCMD -Y 3 -Q "SELECT name, name, name from sys.objects;"
nam nam nam
--- --- ---
sys sys sys
pla pla pla
spt spt spt
fai fai fai
MSr MSr MSr
sp_ sp_ sp_
Run Code Online (Sandbox Code Playgroud)

 
或者,由于这里的目标是将输出作为报告通过电子邮件发送,您可以使用sp_send_dbmail,如下所示:

DECLARE @ReportQuery NVARCHAR(MAX) = N'SET NOCOUNT ON;

PRINT ''<table style="border:1px solid black; width:100%;">
<tr><th>Server</th><th>DatabaseName</th><th>LastBackupDate</th></tr>'';

SELECT CONCAT(
       ''<tr><td>'',
       CONVERT(sysname, SERVERPROPERTY(''Servername'')),
       ''</td><td>'',
       N'''''''' + bset.[database_name] + N'''''''',
       ''</td><td>'',
       MAX(bset.backup_finish_date),
       ''</td></tr>'')
FROM   msdb.dbo.backupmediafamily bfam
INNER JOIN msdb.dbo.backupset bset
       ON bset.media_set_id = bfam.media_set_id
WHERE  bset.[type] = ''D''
GROUP BY      bset.[database_name]
ORDER BY      bset.[database_name];

PRINT ''</table>'';
';

EXEC msdb.dbo.sp_send_dbmail
  @profile_name = N'{your_Profile_name}',
  @recipients = N'{email_address(es)}',
--  @copy_recipients = N'copy_recipient [ ; ...n ]',
--  @blind_copy_recipients = N'blind_copy_recipient [ ; ...n ]',
  @subject = N'Reporty Stuffs', -- NVARCHAR(255)
  @body = N'Here is the report you asked for...',
  @body_format = 'html', -- HTML or TEXT (default)
--  @importance = 'importance', -- Low, Normal (default), or High
--  @sensitivity = 'sensitivity', -- Normal (default), Personal, Private, Confidential
  @query = @ReportQuery,
--  @execute_query_database = N'DB name',
  @query_result_header = 0; -- 0 or 1 (default)
Run Code Online (Sandbox Code Playgroud)

笔记:

  1. 在报告查询中(@ReportQuery在示例中):
    1. SET NOCOUNT ON;是必需的,否则“X 行受影响”在最终</td></tr></table>标签之间打印,弄乱了表格的呈现
    2. CONCAT使构建 HTML 表行变得容易,因为您可以跳过CONVERT(NVARCHAR(x), ...对可以隐式转换的非字符串类型列的操作。对于 SQL Server 2012 之前的版本(CONCAT引入时),只需将CONVERTs 和普通字符串连接到+.
  2. 查询输出将出现在@body变量的内容之后(如果有的话)。
  3. @body_format = 'html'是必需的,否则HTML 标记的<and>将分别转换为&lt;and &gt;,您将看到 HTML 标记(因为它们实际上不是HTML 标记)。
  4. @query_result_header = 0是必需的,否则会在<table>标记和初始之间打印列标题行<tr><td>,从而弄乱表格呈现。