¿Cómo encontrar los objetos más grandes en una base de datos de SQL Server?

136

¿Cómo haría para encontrar los objetos más grandes en una base de datos de SQL Server? Primero, al determinar qué tablas (e índices relacionados) son las más grandes y luego determinar qué filas en una tabla en particular son las más grandes (estamos almacenando datos binarios en BLOB).

¿Existen herramientas para ayudar con este tipo de análisis de bases de datos? ¿O hay algunas consultas simples que podría ejecutar contra las tablas del sistema?

jamesaharvey
fuente

Respuestas:

280

He estado usando este script SQL (que obtuve de alguien, en algún lugar, no puedo reconstruir de quién vino) durante años y me ayudó bastante a comprender y determinar el tamaño de los índices y las tablas:

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) 

Por supuesto, puede usar otros criterios de pedido, por ejemplo

ORDER BY SUM(p.rows) DESC

para obtener las tablas con más filas, o

ORDER BY SUM(a.total_pages) DESC

para obtener las tablas con la mayor cantidad de páginas (bloques de 8K) utilizadas.

marc_s
fuente
¡Excelente gracias! Ahora, que he reducido mi objeto más grande a una tabla que contiene una gran cantidad de datos binarios, de todos modos, ¿para averiguar cuál de las filas de datos binarios es la más grande?
jamesaharvey
3
para eso, tendría que hacer una selección en esa tabla e imprimir DATALENGTH (campo) para cada campo que le interese (generalmente VARCHAR (MAX), VARBINARY (MAX) y así sucesivamente)
marc_s
1
Gracias @marc_s, esto fue muy útil. La columna TableName también puede incluir el nombre del esquema conSELECT OBJECT_SCHEMA_NAME(i.object_id) + '.' + OBJECT_NAME(i.object_id) AS TableName, ...
CruiZen
2
Esa debe ser la secuencia de comandos TSQL más hermosa que he visto nunca
Agustin Meriles
2
Para incluir índices NO AGRUPADOS también, elimine "e i.index_id <= 1" de la cláusula WHERE.
Gordon Bell
72

En SQL Server 2008, también puede ejecutar el informe estándar Uso de disco por tablas superiores. Esto se puede encontrar haciendo clic derecho en la base de datos, seleccionando Informes-> Informes estándar y seleccionando el informe que desee.

Gregory Lancaster
fuente
8
¿En serio? Esa es una respuesta tan revolucionaria. Gracias por publicarlo. (No es sarcasmo. ¡He estado ejecutando estas consultas manualmente durante un tiempo y no puedo creer que estos informes ya estén allí!)
Jennifer Zouak
4

Esta consulta ayuda a encontrar la tabla más grande en su conexión.

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
fuente
Es bueno tener algo que podamos memorizar fácilmente. Gracias por la concisión.
David Betz
3

También puede usar el siguiente código:

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

Si está utilizando Sql Server Management Studio 2008, hay ciertos campos de datos que puede ver en la ventana de detalles del explorador de objetos. Simplemente busque y seleccione la carpeta de tablas. En la vista de detalles, puede hacer clic con el botón derecho en los títulos de las columnas y agregar campos al "informe". Su millaje puede variar si está en SSMS 2008 express.

doug_w
fuente
2

Esta consulta también me pareció muy útil en SqlServerCentral, aquí está el enlace a la publicación original

Tablas más grandes de SQL Server

  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   

En mi base de datos dieron resultados diferentes entre esta consulta y la primera respuesta.

Espero que alguien encuentre útil

Franco
fuente
1

La respuesta de @marc_s es muy buena y la he estado usando durante algunos años. Sin embargo, noté que el script pierde datos en algunos índices de almacén de columnas y no muestra una imagen completa. Por ejemplo, cuando lo hace SUM(TotalSpace)contra el script y lo compara con la propiedad de la base de datos de espacio total en Management Studio, los números no coinciden en mi caso (Management Studio muestra números más grandes). Modifiqué el script para superar este problema y lo extendí un poco:

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

Espero que sea útil para alguien. Este script se probó en grandes bases de datos de TB con cientos de tablas, índices y esquemas diferentes.

dyatchenko
fuente