web-dev-qa-db-fra.com

Format de la longueur de colonne dans la sortie SSMS

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.

Exemple de requête

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

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

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:

  1. Dans la requête de rapport (@ReportQuery Dans l'exemple):
    1. 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 tableau
    2. CONCAT facilite la construction de la ligne du tableau HTML car vous pouvez ignorer faire CONVERT(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 CONVERTs et la concaténation de chaîne normale avec +.
  2. La sortie de la requête viendra après le contenu de la variable @body (S'il y en a).
  3. @body_format = 'html' Est requis, sinon les < Et > Des balises HTML seront traduits respectivement en &lt; Et &gt;, Et vous verra les balises HTML (car elles ne seront pas réellement be balises HTML).
  4. @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.
4
Solomon Rutzky

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
0
Sanjoy