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
_
テキスト出力ビューで列を短く表示する場合は、すべての列で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);
およびがすべての列幅が同じであることを受け入れることができる場合は、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)
_
ノート:
@ReportQuery
_):SET NOCOUNT ON;
_が必要です。それ以外の場合、最後の_</td></tr>
_タグと_</table>
_タグの間に「X行の影響を受ける」と表示され、テーブルのレンダリングが乱れますCONVERT(NVARCHAR(x), ...
をスキップできるため、HTMLテーブル行を簡単に構築できます。 SQL Server 2012より前(CONCAT
が導入されたとき)の場合は、CONVERT
sと通常の文字列連結を_+
_で実行するだけです。@body
_変数(存在する場合)の内容の後に表示されます。@body_format = 'html'
_が必要です。それ以外の場合、HTMLタグの_<
_および_>
_は、それぞれ_<
_および_>
_に変換され、HTMLタグが表示されます(実際にはbeHTMLタグではないため)。@query_result_header = 0
_が必要です。それ以外の場合は、_<table>
_タグと最初の_<tr><td>
_の間に列ヘッダー行が出力され、テーブルのレンダリングが混乱します。特定の長さ形式の出力を取得するために 関数を記述 を使用しています。これが私の解決策です。同じことを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