Afficher la répartition des données et de l'utilisation du disque par table

9

J'ai une base de données SQL Server 2008 R2 utilisée par plusieurs programmes déployés.

Question: Existe-t-il un moyen simple d'afficher la quantité d'espace consommée par chaque table pour toutes les tables de la base de données et de distinguer l'espace logique de l'espace disque?

Si j'utilise SSMS (Management Studio), les propriétés de stockage affichées pour la base de données lisent 167 Mo avec 3 Mo «disponibles» (environ la bonne taille, mais je suis préoccupé par les 3 Mo disponibles - est-ce une limite à se soucier de , en supposant que je sais que j'ai suffisamment d'espace disque?)

Je peux explorer chaque table, mais cela prend une éternité.

Je sais que je peux écrire mes propres requêtes et tester, mais j'aimerais savoir s'il existe déjà un moyen simple (intégré?) De le faire.

Dronz
la source

Réponses:

13

Dans SSMS, faites un clic droit sur la base de données et allez dans "Rapports", "Rapports standard", "Utilisation du disque par table". Il vous indiquera la taille totale, la taille des données, la taille de l'index et la taille inutilisée pour chaque table (ainsi que le nombre de lignes).

nateirvin
la source
1
Je n'avais jamais vu cela auparavant et je l'aime mieux que le script - sauf que sur certaines bases de données, il échoue parce que je n'ai pas l'autorisation "VIEW DATABASE STATE" - mais le script fonctionnera dans ce cas. Allez comprendre!
Chris Woodruff du
10

Il a été répondu sur Stack Overflow:

SELECT 
    t.NAME AS TableName,
    s.Name AS SchemaName,
    p.rows AS RowCounts,
    SUM(a.total_pages) * 8 AS TotalSpaceKB, 
    SUM(a.used_pages) * 8 AS UsedSpaceKB, 
    (SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB
FROM 
    sys.tables t
INNER JOIN      
    sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN 
    sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN 
    sys.allocation_units a ON p.partition_id = a.container_id
LEFT OUTER JOIN 
    sys.schemas s ON t.schema_id = s.schema_id
WHERE 
    t.NAME NOT LIKE 'dt%' 
    AND t.is_ms_shipped = 0
    AND i.OBJECT_ID > 255 
GROUP BY 
    t.Name, s.Name, p.Rows
ORDER BY 
    t.Name
Nelz
la source
5

La requête liée à et copiée par @Nelson est inexacte: elle ignore les vues indexées, les index de texte intégral, les index XML, etc.

Si vous voulez une requête qui inclura tout sans s'exécuter sp_spaceusedvia sp_MSForEachTable, alors j'en ai déjà posté deux variantes (une ici sur DBA.StackExchange et l'autre sur StackOverflow) donc je ne les copierai pas ici:

Solomon Rutzky
la source
4

Juste pour le plaisir, voici une requête qui générera les mêmes données que le rapport dans la réponse de nateirvin

create table #disk_usage
(
    name varchar(128)
    ,rows varchar(20)
    ,reserved varchar(20)
    ,data varchar(20)
    ,index_size varchar(20)
    ,unused varchar(20)
);

exec sp_msforeachtable 'insert into #disk_usage exec sp_spaceused [?]'

select SCHEMA_NAME(st.schema_id) + '.' + du.name 'Table Name'
 ,du.rows '# Records'
 ,du.reserved 'Reserved (KB)'
 ,du.data 'Data (KB)'
 ,du.index_size 'Indexes (KB)'
 ,du.unused 'Unused (KB)'
 from #disk_usage du
left join sys.tables st
on du.name = st.name
order by cast(left(reserved, len(reserved) - 3) as bigint) desc;

drop table #disk_usage

D'accord, parce que je me déteste vraiment , j'ai écrit une requête qui va générer les résultats du rapport, le formater sous forme de tableau HTML et l'envoyer par e-mail. Faire correspondre les couleurs d'arrière-plan du rapport est laissé comme exercice au lecteur.

declare @subject nvarchar(25) = 'Disk Usage by Top Tables';

declare @recipients nvarchar(25) = '[email protected]';

create table #disk_usage
(
    name varchar(128)
    ,rows varchar(20)
    ,reserved varchar(20)
    ,data varchar(20)
    ,index_size varchar(20)
    ,unused varchar(20)
);

exec sp_msforeachtable 'insert into #disk_usage EXEC sp_spaceused [?]'

declare @body nvarchar(max) = 
'<table cellspacing="0">
    <thead>
        <tr>
            <th>Table Name</th>
            <th># Rows</th>
            <th>Reserved</th>
            <th>Data</th>
            <th>Indexes</th>
            <th>Unused</th>
        </tr>
    </thead>
';

set @body = @body + cast (
    (select '<td style="border: 1px solid black; padding: 2px">' + SCHEMA_NAME(s.schema_id) + '.' + t.name + '</td>'
     ,'<td style="border: 1px solid black; padding: 2px">' + rtrim(ltrim(t.rows)) + ' Rows </td>' -- for some reason this was generating a bunch of extra white space and I'm not going to bother to figure out why
     ,'<td style="border: 1px solid black; padding: 2px">' + t.reserved + '</td>'
     ,'<td style="border: 1px solid black; padding: 2px">' + t.data + '</td>'
     ,'<td style="border: 1px solid black; padding: 2px">' + t.index_size + '</td>'
     ,'<td style="border: 1px solid black; padding: 2px">' + t.unused + '</td>'
     from #disk_usage t
    left join sys.tables s
    on t.name = s.name
    order by cast(left(reserved, len(reserved) - 3) as bigint) desc
    for xml path ('tr'))
as nvarchar(max));

set @body = replace(replace(@body, '&lt;', '<'), '&gt;', '>')
set @body = @body + '</table>'

exec msdb.dbo.sp_send_dbmail 
@profile_name='A Database Mail Profile On The Target Server', 
@recipients=@recipients, 
@subject=@subject,
@body=@body,
@body_format='HTML'

drop table #disk_usage
MikeTheLiar
la source
1
Hou la la! Ce dernier morceau de travail auto-détestable est merveilleux (pour les autres ;-))! Merci d'avoir ajouté ça. Je regrette de n'avoir qu'une seule voix positive à donner à cette réponse ...
Dronz
1
:) J'ai pensé que si je devais endurer la peine de le faire, le moins que je puisse faire est de sauver quelqu'un d'autre du même sort.
MikeTheLiar