Comment trouver les plus gros objets dans une base de données SQL Server?

136

Comment procéder pour rechercher les objets les plus volumineux dans une base de données SQL Server? Premièrement, en déterminant quelles tables (et index associés) sont les plus grandes, puis en déterminant quelles lignes d'une table particulière sont les plus grandes (nous stockons des données binaires dans des BLOB)?

Existe-t-il des outils pour aider à ce type d'analyse de base de données? Ou y a-t-il des requêtes simples que je pourrais exécuter sur les tables système?

Jamesaharvey
la source

Réponses:

280

J'utilise ce script SQL (que j'ai obtenu de quelqu'un, quelque part - je ne peux pas reconstruire de qui il vient) depuis des lustres et cela m'a beaucoup aidé à comprendre et à déterminer la taille des index et des tables:

SELECT 
    t.name AS TableName,
    i.name as indexName,
    sum(p.rows) as RowCounts,
    sum(a.total_pages) as TotalPages, 
    sum(a.used_pages) as UsedPages, 
    sum(a.data_pages) as DataPages,
    (sum(a.total_pages) * 8) / 1024 as TotalSpaceMB, 
    (sum(a.used_pages) * 8) / 1024 as UsedSpaceMB, 
    (sum(a.data_pages) * 8) / 1024 as DataSpaceMB
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
WHERE 
    t.name NOT LIKE 'dt%' AND
    i.object_id > 255 AND  
    i.index_id <= 1
GROUP BY 
    t.name, i.object_id, i.index_id, i.name 
ORDER BY 
    object_name(i.object_id) 

Bien sûr, vous pouvez utiliser un autre critère de commande, par exemple

ORDER BY SUM(p.rows) DESC

pour obtenir les tables avec le plus de lignes, ou

ORDER BY SUM(a.total_pages) DESC

pour obtenir les tables avec le plus de pages (blocs de 8K) utilisées.

marc_s
la source
Excellent merci! Maintenant que j'ai réduit mon plus gros objet à une table contenant beaucoup de données binaires, de toute façon pour déterminer laquelle des lignes de données binaires est la plus grande?
jamesaharvey
3
pour cela, vous devez faire une sélection sur cette table et imprimer DATALENGTH (champ) pour chaque champ qui vous intéresse (généralement VARCHAR (MAX), VARBINARY (MAX) et ainsi de suite)
marc_s
1
Merci @marc_s, cela a été très utile. La colonne TableName peut également inclure le nom du schéma avecSELECT OBJECT_SCHEMA_NAME(i.object_id) + '.' + OBJECT_NAME(i.object_id) AS TableName, ...
CruiZen
2
Ce doit être le plus beau script TSQL que j'ai jamais vu
Agustin Meriles
2
Pour inclure également les index NON-CLUSTERED, supprimez "et i.index_id <= 1" de la clause WHERE.
Gordon Bell
72

Dans SQL Server 2008, vous pouvez également simplement exécuter le rapport standard Utilisation du disque par les principales tables. Vous pouvez le trouver en cliquant avec le bouton droit sur la base de données, en sélectionnant Rapports-> Rapports standard et en sélectionnant le rapport souhaité.

Gregory Lancaster
la source
8
Sans blague? C'est une réponse tellement révolutionnaire. Merci de l'avoir publié. (Pas de sarcasme. J'exécute ces requêtes manuellement depuis un moment maintenant et je ne peux pas croire que ces rapports soient déjà là!)
Jennifer Zouak
4

Cette requête aide à trouver la plus grande table dans votre connexion.

SELECT  TOP 1 OBJECT_NAME(OBJECT_ID) TableName, st.row_count
FROM sys.dm_db_partition_stats st
WHERE index_id < 2
ORDER BY st.row_count DESC
Vinoth_S
la source
C'est bien d'avoir quelque chose que l'on peut facilement mémoriser. Merci pour la concision.
David Betz
3

Vous pouvez également utiliser le code suivant:

USE AdventureWork
GO
CREATE TABLE #GetLargest 
(
  table_name    sysname ,
  row_count     INT,
  reserved_size VARCHAR(50),
  data_size     VARCHAR(50),
  index_size    VARCHAR(50),
  unused_size   VARCHAR(50)
)

SET NOCOUNT ON

INSERT #GetLargest

EXEC sp_msforeachtable 'sp_spaceused ''?'''

SELECT 
  a.table_name,
  a.row_count,
  COUNT(*) AS col_count,
  a.data_size
  FROM #GetLargest a
     INNER JOIN information_schema.columns b
     ON a.table_name collate database_default
     = b.table_name collate database_default
       GROUP BY a.table_name, a.row_count, a.data_size
       ORDER BY CAST(REPLACE(a.data_size, ' KB', '') AS integer) DESC

DROP TABLE #GetLargest
Dheeraj Bansal
la source
2

Si vous utilisez Sql Server Management Studio 2008, vous pouvez afficher certains champs de données dans la fenêtre de détails de l'explorateur d'objets. Recherchez et sélectionnez simplement le dossier des tables. Dans la vue détaillée, vous pouvez cliquer avec le bouton droit sur les titres des colonnes et ajouter des champs au «rapport». Votre kilométrage peut varier si vous utilisez SSMS 2008 express.

doug_w
la source
2

J'ai trouvé cette requête également très utile dans SqlServerCentral, voici le lien vers l'article original

Tables les plus volumineuses du serveur SQL

  select name=object_schema_name(object_id) + '.' + object_name(object_id)
, rows=sum(case when index_id < 2 then row_count else 0 end)
, reserved_kb=8*sum(reserved_page_count)
, data_kb=8*sum( case 
     when index_id<2 then in_row_data_page_count + lob_used_page_count + row_overflow_used_page_count 
     else lob_used_page_count + row_overflow_used_page_count 
    end )
, index_kb=8*(sum(used_page_count) 
    - sum( case 
           when index_id<2 then in_row_data_page_count + lob_used_page_count + row_overflow_used_page_count 
        else lob_used_page_count + row_overflow_used_page_count 
        end )
     )    
, unused_kb=8*sum(reserved_page_count-used_page_count)
from sys.dm_db_partition_stats
where object_id > 1024
group by object_id
order by 
rows desc   

Dans ma base de données, ils ont donné des résultats différents entre cette requête et la première réponse.

J'espère que quelqu'un trouvera utile

Franco
la source
1

La réponse de @ marc_s est très bonne et je l'utilise depuis quelques années. Cependant, j'ai remarqué que le script manque des données dans certains index columnstore et ne montre pas une image complète. Par exemple, lorsque vous SUM(TotalSpace)comparez le script avec la propriété de base de données d'espace total dans Management Studio, les nombres ne correspondent pas dans mon cas (Management Studio affiche des nombres plus grands). J'ai modifié le script pour résoudre ce problème et l'ai étendu un peu:

select
    tables.[name] as table_name,
    schemas.[name] as schema_name,
    isnull(db_name(dm_db_index_usage_stats.database_id), 'Unknown') as database_name,
    sum(allocation_units.total_pages) * 8 as total_space_kb,
    cast(round(((sum(allocation_units.total_pages) * 8) / 1024.00), 2) as numeric(36, 2)) as total_space_mb,
    sum(allocation_units.used_pages) * 8 as used_space_kb,
    cast(round(((sum(allocation_units.used_pages) * 8) / 1024.00), 2) as numeric(36, 2)) as used_space_mb,
    (sum(allocation_units.total_pages) - sum(allocation_units.used_pages)) * 8 as unused_space_kb,
    cast(round(((sum(allocation_units.total_pages) - sum(allocation_units.used_pages)) * 8) / 1024.00, 2) as numeric(36, 2)) as unused_space_mb,
    count(distinct indexes.index_id) as indexes_count,
    max(dm_db_partition_stats.row_count) as row_count,
    iif(max(isnull(user_seeks, 0)) = 0 and max(isnull(user_scans, 0)) = 0 and max(isnull(user_lookups, 0)) = 0, 1, 0) as no_reads,
    iif(max(isnull(user_updates, 0)) = 0, 1, 0) as no_writes,
    max(isnull(user_seeks, 0)) as user_seeks,
    max(isnull(user_scans, 0)) as user_scans,
    max(isnull(user_lookups, 0)) as user_lookups,
    max(isnull(user_updates, 0)) as user_updates,
    max(last_user_seek) as last_user_seek,
    max(last_user_scan) as last_user_scan,
    max(last_user_lookup) as last_user_lookup,
    max(last_user_update) as last_user_update,
    max(tables.create_date) as create_date,
    max(tables.modify_date) as modify_date
from 
    sys.tables
    left join sys.schemas on schemas.schema_id = tables.schema_id
    left join sys.indexes on tables.object_id = indexes.object_id
    left join sys.partitions on indexes.object_id = partitions.object_id and indexes.index_id = partitions.index_id
    left join sys.allocation_units on partitions.partition_id = allocation_units.container_id
    left join sys.dm_db_index_usage_stats on tables.object_id = dm_db_index_usage_stats.object_id and indexes.index_id = dm_db_index_usage_stats.index_id
    left join sys.dm_db_partition_stats on tables.object_id = dm_db_partition_stats.object_id and indexes.index_id = dm_db_partition_stats.index_id
group by schemas.[name], tables.[name], isnull(db_name(dm_db_index_usage_stats.database_id), 'Unknown')
order by 5 desc

J'espère que cela sera utile pour quelqu'un. Ce script a été testé sur de grandes bases de données à l'échelle du TB avec des centaines de tables, d'index et de schémas différents.

Dyatchenko
la source