Wie finde ich die größten Objekte in einer SQL Server-Datenbank?

136

Wie würde ich vorgehen, um die größten Objekte in einer SQL Server-Datenbank zu finden? Erstens, indem Sie bestimmen, welche Tabellen (und zugehörigen Indizes) die größten sind, und dann bestimmen, welche Zeilen in einer bestimmten Tabelle am größten sind (wir speichern Binärdaten in BLOBs)?

Gibt es Tools, die bei dieser Art der Datenbankanalyse helfen? Oder gibt es einige einfache Abfragen, die ich für die Systemtabellen ausführen könnte?

Jamesaharvey
quelle

Antworten:

279

Ich benutze dieses SQL-Skript (das ich irgendwo von jemandem bekommen habe - ich kann nicht rekonstruieren, von wem es stammt) seit Ewigkeiten und es hat mir einiges geholfen, die Größe von Indizes und Tabellen zu verstehen und zu bestimmen:

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) 

Natürlich können Sie auch andere Bestellkriterien verwenden, z

ORDER BY SUM(p.rows) DESC

um die Tabellen mit den meisten Zeilen zu erhalten, oder

ORDER BY SUM(a.total_pages) DESC

um die Tabellen mit den meisten verwendeten Seiten (8K-Blöcke) abzurufen.

marc_s
quelle
Ausgezeichnet, vielen Dank! Nun, da ich mein größtes Objekt auf eine Tabelle eingegrenzt habe, die viele Binärdaten enthält, um herauszufinden, welche der Binärdatenzeilen die größten sind?
Jamesaharvey
3
Dazu müssten Sie eine Auswahl für diese Tabelle treffen und die DATALENGTH (Feld) für jedes Feld ausdrucken, an dem Sie interessiert sind (normalerweise VARCHAR (MAX), VARBINARY (MAX) usw.)
marc_s
1
Danke @marc_s, das war sehr nützlich. Die TableName-Spalte kann auch den Schemanamen mitSELECT OBJECT_SCHEMA_NAME(i.object_id) + '.' + OBJECT_NAME(i.object_id) AS TableName, ...
CruiZen
2
Das muss das schönste TSQL-Skript sein, das ich je gesehen habe
Agustin Meriles
2
Entfernen Sie "und i.index_id <= 1" aus der WHERE-Klausel, um auch NON-CLUSTERED-Indizes einzuschließen.
Gordon Bell
72

In SQL Server 2008 können Sie auch einfach den Standardbericht Datenträgerverwendung nach Top-Tabellen ausführen. Sie finden dies, indem Sie mit der rechten Maustaste auf die Datenbank klicken , Berichte-> Standardberichte auswählen und den gewünschten Bericht auswählen.

Gregory Lancaster
quelle
8
Im Ernst? Das ist so eine revolutionäre Antwort. Danke, dass du es gepostet hast. (Kein Sarkasmus. Ich habe diese Abfragen schon eine Weile manuell ausgeführt und ich kann nicht glauben, dass diese Berichte bereits vorhanden sind!)
Jennifer Zouak
4

Diese Abfrage hilft, die größte Tabelle in Ihrer Verbindung zu finden.

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
quelle
Es ist gut, etwas zu haben, das wir uns leicht merken können. Danke für die Prägnanz.
David Betz
3

Sie können auch den folgenden Code verwenden:

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

Wenn Sie SQL Server Management Studio 2008 verwenden, gibt es bestimmte Datenfelder, die Sie im Objekt-Explorer-Detailfenster anzeigen können. Navigieren Sie einfach zum Tabellenordner und wählen Sie ihn aus. In der Detailansicht können Sie mit der rechten Maustaste auf die Spaltentitel klicken und dem "Bericht" Felder hinzufügen. Ihr Kilometerstand kann variieren, wenn Sie mit SSMS 2008 Express arbeiten.

doug_w
quelle
2

Ich fand diese Abfrage auch in SqlServerCentral sehr hilfreich. Hier ist der Link zum ursprünglichen Beitrag

SQL Server größte Tabellen

  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   

In meiner Datenbank gaben sie unterschiedliche Ergebnisse zwischen dieser Abfrage und der ersten Antwort.

Hoffe, jemand findet nützlich

Franco
quelle
1

Die Antwort von @ marc_s ist sehr gut und ich benutze sie seit einigen Jahren. Ich habe jedoch festgestellt, dass das Skript Daten in einigen Columnstore-Indizes vermisst und kein vollständiges Bild anzeigt. Wenn Sie beispielsweise SUM(TotalSpace)das Skript mit der Eigenschaft "Gesamtspeicherdatenbank" in Management Studio vergleichen, stimmen die Zahlen in meinem Fall nicht überein (Management Studio zeigt größere Zahlen an). Ich habe das Skript geändert, um dieses Problem zu beheben, und es ein wenig erweitert:

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

Hoffe, es wird für jemanden hilfreich sein. Dieses Skript wurde gegen große TB-weite Datenbanken mit Hunderten verschiedener Tabellen, Indizes und Schemata getestet.

dyatchenko
quelle