web-dev-qa-db-ja.com

SSMS出力の列の長さをフォーマットする

SQL Server2012。この投稿の下部にあるサンプルクエリ。

特定のデータベースが最後にバックアップされたときの簡単なレポートを作成しようとしています。

SSMSでtext to outputを使用してサンプルクエリを実行すると、_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, 
...
_

ただし、SQL Serverでは、キャスト時にvarchar定義で変数_@database_name_Length_を使用できません。 SQL Serverは、charまたはvarchar変数を宣言するときに、リテラル数を要求するようです。

文字列でステートメントを作成し、_sp_executesql_のようなものを使用するか、必要な実際の列の長さを使用して一時テーブルを作成することにしました。 128文字の列の出力で100のスペースを取得しないようにするだけです。

インターウェブを検索し、bupkusを見つけました。

たぶん私は間違ったものを探しているのかもしれませんし、Googleが私に干渉しているのかもしれません。

実際のデータがはるかに小さい場合でも、SSMSは列を許可される最大サイズにフォーマットするようです。私は、フープを飛び越えずにこれを「修正」するエレガントな方法を望んでいました。 SSMS 2012を使用しています。

Results To Gridに移動してから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 
_
4
Joe Hayes

テキスト出力ビューで列を短く表示する場合は、すべての列で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;

これにより、次のような出力が得られます。

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)

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

ここでは、すべての列の幅を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)

本当に気が狂って、列のサイズを自動的に自分自身に設定したい場合は、次のようにします。

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);
6
Max Vernon

およびがすべての列幅が同じであることを受け入れることができる場合は、SQLCMDの_-Y_オプションを試してください.exe

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


または、ここでの目的は出力をレポートとして電子メールで送信することなので、次のように 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)
_

ノート:

  1. レポートクエリ(例では_@ReportQuery_):
    1. _SET NOCOUNT ON;_が必要です。それ以外の場合、最後の_</td></tr>_タグと_</table>_タグの間に「X行の影響を受ける」と表示され、テーブルのレンダリングが乱れます
    2. [〜#〜] concat [〜#〜] は、暗黙的に変換できるタイプの非文字列列に対してCONVERT(NVARCHAR(x), ...をスキップできるため、HTMLテーブル行を簡単に構築できます。 SQL Server 2012より前(CONCATが導入されたとき)の場合は、CONVERTsと通常の文字列連結を_+_で実行するだけです。
  2. クエリ出力は、_@body_変数(存在する場合)の内容の後に表示されます。
  3. _@body_format = 'html'_が必要です。それ以外の場合、HTMLタグの_<_および_>_は、それぞれ_&lt;_および_&gt;_に変換され、HTMLタグが表示されます(実際にはbeHTMLタグではないため)。
  4. _@query_result_header = 0_が必要です。それ以外の場合は、_<table>_タグと最初の_<tr><td>_の間に列ヘッダー行が出力され、テーブルのレンダリングが混乱します。
4
Solomon Rutzky

特定の長さ形式の出力を取得するために 関数を記述 を使用しています。これが私の解決策です。同じことをSQLクエリで使用できます。

CREATE Function [dbo].[UFN_COLUMNFORMAT]        
(@format nVARCHAR(10), @value nVARCHAR(100))      
Returns nVarchar(max)      
 As      
 Begin      
 DECLARE  @value1 VARCHAR(100), @Fchar VARCHAR(5), @innerchar VARCHAR(5), @symbol VARCHAR(1),        
 @leninnerchar INT, @FFormat CHAR(20), @m INT, @n INT, @sql NVARCHAR(max),@ValueFordate datetime      

 SELECT @Fchar = Substring(@format, 1, Charindex('(', @format) - 1)      

 SELECT @innerchar = Substring(@format, Charindex('(', @format) + 1, ((Charindex(')', @format) - Charindex('(', @format)) - 1))      

 SELECT @symbol = Substring(@format, Charindex(')', @format) + 1, len(@format))      


 SELECT @m = CASE        
   WHEN charindex('#', @innerchar) > 0      
    THEN substring(@innerchar, 1, charindex('#', @innerchar) - 1)      
   WHEN charindex(',', @innerchar) > 0      
    THEN substring(@innerchar, 1, charindex(',', @innerchar) - 1)      
   ELSE 0      
   END      

 SELECT @n = CASE        
   WHEN charindex('#', @innerchar) > 0      
    THEN substring(@innerchar, charindex('#', @innerchar) + 1, len(@innerchar))      
   WHEN charindex(',', @innerchar) > 0      
    THEN substring(@innerchar, charindex(',', @innerchar) + 1, len(@innerchar))      
   ELSE @innerchar      
   END      



 SELECT @FFormat = CASE        
   WHEN @Fchar = 'A'      
    THEN 'A'      
   WHEN @Fchar = 'A0'      
    THEN 'A0'      
   WHEN @Fchar = 'N'      
    THEN 'N'      
   WHEN @Fchar = 'Date'      
    THEN 'Date'      
   END + '(' + CASE        
   WHEN charindex('#', @innerchar) > 0      
    THEN 'm#n'      
   WHEN charindex(',', @innerchar) > 0      
    THEN 'm,n'      
   ELSE 'n'      
   END + ')' + CASE        
   WHEN @symbol = ''      
    THEN ''      
   ELSE @symbol      
   END      


   IF @FFormat='A(n)'      

   Select @value1=  LEFT(CAST( @value   AS VARCHAR( max)) + REPLICATE(' ', @n) ,@n)      
   IF @FFormat='A(n)+'      

   Select @value1=   LEFT(@symbol+CAST( @value   AS VARCHAR( max) )+ REPLICATE(' ', @n),@n)      
   IF @FFormat='A0(n)'      

   Select @value1=   LEFT(CAST( @value   AS VARCHAR( max)) +REPLICATE('0', @n),@n)      
   IF @FFormat='A0(n)+'      

   Select @value1=    @symbol+LEFT( REPLICATE('0', @n-1) +CAST( @value   AS VARCHAR( max) ),@n-1)      
   IF @FFormat='A(m#n)'      

   Select @value1=    LEFT(CAST( replace(@value,'.','')   AS VARCHAR( max) )+ REPLICATE(' ',@m),@m)      
    IF @FFormat='A0(m#n)'      

   Select @value1=     LEFT( CAST( replace(@value,'.','')   AS VARCHAR( max) )+REPLICATE('0',@m),@m)      
    IF @FFormat='A0(m,n)+'      


   Select @value1=     @symbol+  LEFT( REPLICATE('0',@m-@n-1) +CAST( substring(@value,1,CHARINDEX('.',@value))   AS VARCHAR( max) ),@m-@n-1)+ Left(  CAST( substring(@value,CHARINDEX('.',@value)+1,len(@value))   AS VARCHAR( max) )+REPLICATE('0', @n) , @n)

   IF @FFormat='A0(m,n)'      


   Select @value1=     LEFT( CAST( substring(@value,1,CHARINDEX('.',@value))   AS VARCHAR( max) ) + REPLICATE('0',@m-@n) ,@m-@n)+ Left(  CAST( substring(@value,CHARINDEX('.',@value)+1,len(@value))   AS VARCHAR( max) )+REPLICATE('0', @n) , @n)      
   IF @FFormat='N(m,n)'      

   Select @value1=      LEFT(CAST( @value   AS VARCHAR( max) )+REPLICATE('0',@m-1),@m-1)      
    IF @FFormat='N(n)'      

   Select @value1=      LEFT(CAST( @value   AS VARCHAR( max) )+REPLICATE(' ',@n),@n)      
   IF @FFormat='Date(n)'      
   select @value1=  convert(varchar(50),convert(datetime,@value),112)
   IF @FFormat='Date(n)'      
   select @value1=  convert(varchar(50),convert(datetime,@value),20)          
   Return @value1      
 End
0
Sanjoy