SQL Server 2012. Exemple de requête au bas de cet article.
J'essaie de créer un rapport simple pour la dernière sauvegarde d'une base de données.
Lors de l'exécution de l'exemple de requête avec sortie en texte dans SSMS, la colonne DB_NAME
Est formatée pour être la taille maximale possible pour les données (le même problème existe dans DB2, btw). Donc, j'ai une colonne qui contient des données qui ne sont jamais plus de, disons, 12 caractères, mais elles sont stockées dans une varchar(128)
, j'obtiens 128 caractères de données quoi qu'il arrive. RTRIM
n'a aucun effet sur la sortie.
Existe-t-il un moyen élégant que vous connaissiez pour que la longueur de colonne formatée soit la taille maximale des données réelles, plutôt que la taille potentielle maximale des données?
Je suppose qu'il existe une fonction xp_sprintf()
, mais je ne la connais pas et elle n'a pas l'air terriblement robuste.
J'ai essayé de le lancer comme ceci:
DECLARE @Servername_Length int;
SELECT @Servername_Length = LEN( CAST( SERVERPROPERTY('Servername') AS VARCHAR(MAX) ) ) ;
...
SELECT
CONVERT(CHAR(@Servername_Length), SERVERPROPERTY('Servername')) AS Server,
...
Mais alors, SQL Server ne me laissera pas utiliser la variable @database_name_Length
Dans ma définition varchar
lors de la conversion. SQL Server, apparemment, exige un nombre littéral lors de la déclaration de la variable char
ou varchar
.
Je suis en train de construire l'instruction dans une chaîne et d'utiliser quelque chose comme sp_executesql
, Ou de construire une table temporaire avec les longueurs de colonne dont j'ai besoin, les deux étant vraiment un peu plus difficiles que je ne l'espérais juste pour ne PAS obtenir 100 espaces dans ma sortie sur une colonne de 128 caractères.
J'ai fouillé les interwebs et trouvé des bupkus.
Peut-être que je cherche la mauvaise chose, ou Google est en colère contre moi.
Il semble que SSMS formatera la colonne pour qu'elle soit à la taille maximale autorisée, même si les données réelles sont beaucoup plus petites. J'espérais une manière élégante de "corriger" cela sans sauter à travers des cerceaux. J'utilise SSMS 2012.
Si je vais dans Résultats vers la grille puis vers Excel ou quelque chose de similaire, l'espace de fin est éliminé. J'espérais cependant créer un rapport que j'enverrais par courrier électronique.
--------------------------------------------------------------------------
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
Vous devez utiliser CONVERT(VARCHAR(xx), ColumnName)
sur toutes les colonnes si vous souhaitez que la colonne apparaisse plus courte dans la vue de sortie de texte.
Convertissez votre requête en quelque chose comme:
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;
Cela donnera une sortie similaire à:
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)
Si vous souhaitez pouvoir modifier dynamiquement la largeur des colonnes sans modifier le code T-SQL, vous devrez utiliser Dynamic 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);
Ici, j'ai défini la largeur à 24 pour toutes les colonnes, et cela ressemble à ceci:
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)
Si vous voulez vraiment devenir fou et que les colonnes se redimensionnent automatiquement, vous feriez ceci:
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);
Si vous voulez quelque chose de rapide et facile, et peut accepter que toutes les largeurs de colonne soient les mêmes, alors essayez l'option -Y
De SQLCMD.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_
OU, puisque l'objectif ici est d'envoyer la sortie sous forme de rapport, vous pouvez utiliser sp_send_dbmail comme suit:
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)
Remarques:
@ReportQuery
Dans l'exemple): SET NOCOUNT ON;
Est requis, sinon "X ligne (s) affectée (s)" s'imprime entre la dernière </td></tr>
Et la balise </table>
, Gâchant le rendu du tableauCONVERT(NVARCHAR(x), ...
pour les colonnes non chaîne de types qui peuvent implicitement convertir . Pour pré-SQL Server 2012 (lorsque CONCAT
a été introduit), effectuez simplement la CONVERT
s et la concaténation de chaîne normale avec +
.@body
(S'il y en a).@body_format = 'html'
Est requis, sinon les <
Et >
Des balises HTML seront traduits respectivement en <
Et >
, Et vous verra les balises HTML (car elles ne seront pas réellement be balises HTML).@query_result_header = 0
Est requis, sinon une ligne d'en-tête de colonne s'imprime entre la balise <table>
Et le <tr><td>
Initial, gâchant le rendu du tableau.J'ai écrit une fonction pour obtenir une sortie de format de longueur spécifique. Voici ma solution. Vous pouvez utiliser la même chose dans votre requête 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