Los intentos de reclamar espacio no utilizado hace que el espacio utilizado aumente significativamente en SQL Server


15

Tengo una tabla en una base de datos de producción que tiene un tamaño de 525 GB, de los cuales 383 GB no se utilizan:

Espacio no utilizado

Me gustaría recuperar parte de este espacio, pero, antes de jugar con el DB de producción, estoy probando algunas estrategias en una tabla idéntica en un DB de prueba con menos datos. Esta tabla tiene un problema similar:

Espacio no utilizado

Alguna información sobre la tabla:

  • El factor de relleno se establece en 0
  • Hay alrededor de 30 columnas
  • Una de las columnas es una imagen de tipo LOB, y almacena archivos que varían en tamaño desde unos pocos KB hasta varios cientos de MB
  • La tabla no tiene ningún índice hipotético asociado.

El servidor ejecuta SQL Server 2017 (RTM-GDR) (KB4505224) - 14.0.2027.2 (X64). La base de datos está utilizando el SIMPLEmodelo de recuperación.

Algunas cosas que he probado:

  • La reconstrucción de los índices: ALTER INDEX ALL ON dbo.MyTable REBUILD. Esto tuvo un impacto insignificante.
  • La reorganización de los índices: ALTER INDEX ALL ON dbo.MyTable REORGANIZE WITH(LOB_COMPACTION = ON). Esto tuvo un impacto insignificante.
  • Copié la columna LOB en otra tabla, soltó la columna, volvió a crear la columna y volvió a copiar los datos (como se describe en esta publicación: Liberación de la tabla de SQL Server del espacio no utilizado ). Esto disminuyó el espacio no utilizado, pero parecía convertirlo en espacio usado:

    Espacio no utilizado

  • Usó la utilidad bcp para exportar la tabla, truncarla y volver a cargarla (como se describe en esta publicación: Cómo liberar el espacio no utilizado para una tabla ). Esto también redujo el espacio no utilizado y aumentó el espacio utilizado en un grado similar al de la imagen de arriba.

  • Aunque no se recomienda, probé los comandos DBCC SHRINKFILE y DBCC SHRINKDATABASE, pero no tuvieron ningún impacto en el espacio no utilizado.
  • Correr DBCC CLEANTABLE('myDB', 'dbo.myTable')no hizo la diferencia
  • He intentado todo lo anterior mientras mantengo los tipos de datos de imagen y texto y después de cambiar los tipos de datos a varbinary (max) y varchar (max).
  • Intenté importar los datos a una nueva tabla en una nueva base de datos, y esto también solo convirtió el espacio no utilizado en espacio usado. Describí los detalles de este intento en esta publicación .

No quiero hacer estos intentos en la base de datos de producción si estos son los resultados que puedo esperar, así que:

  1. ¿Por qué el espacio no utilizado se está convirtiendo en espacio usado después de algunos de estos intentos? Siento que no entiendo bien lo que sucede debajo del capó.
  2. ¿Hay algo más que pueda hacer para disminuir el espacio no utilizado sin aumentar el espacio utilizado?

EDITAR: Aquí está el informe de uso del disco y la secuencia de comandos para la tabla:

Uso del disco

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[MyTable](
    [Column1]  [int] NOT NULL,
    [Column2]  [int] NOT NULL,
    [Column3]  [int] NOT NULL,
    [Column4]  [bit] NOT NULL,
    [Column5]  [tinyint] NOT NULL,
    [Column6]  [datetime] NULL,
    [Column7]  [int] NOT NULL,
    [Column8]  [varchar](100) NULL,
    [Column9]  [varchar](256) NULL,
    [Column10] [int] NULL,
    [Column11] [image] NULL,
    [Column12] [text] NULL,
    [Column13] [varchar](100) NULL,
    [Column14] [varchar](6) NULL,
    [Column15] [int] NOT NULL,
    [Column16] [bit] NOT NULL,
    [Column17] [datetime] NULL,
    [Column18] [varchar](50) NULL,
    [Column19] [varchar](50) NULL,
    [Column20] [varchar](60) NULL,
    [Column21] [varchar](20) NULL,
    [Column22] [varchar](120) NULL,
    [Column23] [varchar](4) NULL,
    [Column24] [varchar](75) NULL,
    [Column25] [char](1) NULL,
    [Column26] [varchar](50) NULL,
    [Column27] [varchar](128) NULL,
    [Column28] [varchar](50) NULL,
    [Column29] [int] NULL,
    [Column30] [text] NULL,
 CONSTRAINT [PK] PRIMARY KEY CLUSTERED 
(
    [Column1] ASC,
    [Column2] ASC,
    [Column3] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column4]  DEFAULT (0) FOR [Column4]
GO
ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column5]  DEFAULT (0) FOR [Column5]
GO
ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column15]  DEFAULT (0) FOR [Column15]
GO
ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column16]  DEFAULT (0) FOR [Column16]
GO

Estos son los resultados de ejecutar los comandos en la respuesta de Max Vernon:

╔════════════╦═══════════╦════════════╦═════════════════╦══════════════════════╦════════════════════╗
 TotalBytes  FreeBytes  TotalPages  TotalEmptyPages  PageBytesFreePercent  UnusedPagesPercent 
╠════════════╬═══════════╬════════════╬═════════════════╬══════════════════════╬════════════════════╣
  9014280192 8653594624     1100376          997178             95.998700           90.621500 
╚════════════╩═══════════╩════════════╩═════════════════╩══════════════════════╩════════════════════╝
╔═════════════╦═══════════════════╦════════════════════╗
 ObjectName   ReservedPageCount       UsedPageCount 
╠═════════════╬═══════════════════╬════════════════════╣
 dbo.MyTable            5109090             2850245 
╚═════════════╩═══════════════════╩════════════════════╝

ACTUALIZAR:

Ejecuté lo siguiente según lo sugerido por Max Vernon:

DBCC UPDATEUSAGE (N'<database_name>', N'<table_name>');

Y aquí estaba el resultado:

DBCC UPDATEUSAGE: Usage counts updated for table 'MyTable' (index 'PK_MyTable', partition 1):
        USED pages (LOB Data): changed from (568025) to (1019641) pages.
        RSVD pages (LOB Data): changed from (1019761) to (1019763) pages.

Esto actualizó el uso del disco para la tabla:

ingrese la descripción de la imagen aquí

Y el uso general del disco:

ingrese la descripción de la imagen aquí

Por lo tanto, parece que el problema fue que el uso del disco según lo rastreado por SQL Server se desincronizó con el uso real del disco. Consideraré este problema resuelto, ¡pero me interesaría saber por qué esto habría sucedido en primer lugar!

Respuestas:


10

Había corrido DBCC UPDATEUSAGE contra la mesa, como primer paso, ya que los síntomas muestran el uso del espacio inconsistente.

DBCC UPDATEUSAGE corrige las filas, páginas usadas, páginas reservadas, páginas de hoja y recuentos de páginas de datos para cada partición en una tabla o índice. Si no hay imprecisiones en las tablas del sistema, DBCC UPDATEUSAGE no devuelve datos. Si se encuentran y corrigen imprecisiones y no se utiliza WITH NO_INFOMSGS, DBCC UPDATEUSAGE devuelve las filas y columnas que se actualizan en las tablas del sistema.

La sintaxis es:

DBCC UPDATEUSAGE (N'<database_name>', N'<table_name>');

Después de que corras eso, correría EXEC sys.sp_spaceusedcontra la mesa:

EXEC sys.sp_spaceused @objname = N'dbo.MyTable'
    , @updateusage = 'false' --true or false
    , @mode = 'ALL' --ALL, LOCAL_ONLY, REMOTE_ONLY
    , @oneresultset = 1;

El comando anterior tiene la opción de actualizar el uso, pero dado que DBCC UPDATEUSAGEprimero ejecutó manualmente, simplemente deje ese conjunto en falso. La ejecución DBCC UPDATEUSAGEmanual le permite ver si se corrigió algo.

La siguiente consulta debe mostrar el porcentaje de bytes libres en la tabla y el porcentaje de páginas libres en la tabla. Dado que la consulta usa una característica no documentada, no es prudente contar con los resultados, pero parece preciso cuando se compara con la salida de sys.sp_spaceused, en un nivel alto.

Si el porcentaje de bytes libres es significativamente mayor que el porcentaje de páginas libres, entonces tiene muchas páginas parcialmente vacías.

Las páginas parcialmente vacías pueden provenir de una serie de causas, que incluyen:

  1. Divisiones de página, donde la página debe dividirse para acomodar nuevas inserciones en el índice agrupado

  2. Incapacidad para llenar la página con columnas debido al tamaño de la columna.

La consulta utiliza la sys.dm_db_database_page_allocationsfunción de gestión dinámica no documentada :

;WITH dpa AS 
(
    SELECT dpa.*
        , page_free_space_percent_corrected = 
          CASE COALESCE(dpa.page_type_desc, N'')
            WHEN N'TEXT_MIX_PAGE' THEN 100 - COALESCE(dpa.page_free_space_percent, 100)
            WHEN N'TEXT_TREE_PAGE' THEN 100 - COALESCE(dpa.page_free_space_percent, 100)
            ELSE COALESCE(dpa.page_free_space_percent, 100)
          END
    FROM sys.dm_db_database_page_allocations(DB_ID(), OBJECT_ID('dbo.MyTable'), NULL, NULL, 'DETAILED') dpa
)
, src AS
(
SELECT TotalKB = COUNT_BIG(1) * 8192 / 1024
    , FreeKB = SUM((dpa.page_free_space_percent_corrected / 100) * CONVERT(bigint, 8192)) / 1024
    , TotalPages = COUNT_BIG(1)
    , TotalEmptyPages = SUM(CASE WHEN dpa.page_free_space_percent_corrected = 100 THEN 1 ELSE 0 END) --completely empty pages
FROM dpa
)
SELECT *
    , BytesFreePercent = (CONVERT(decimal(38,2), src.FreeKB) / src.TotalKB) * 100
    , UnusedPagesPercent = (CONVERT(decimal(38,2), src.TotalEmptyPages) / src.TotalPages) * 100
FROM src

La salida se ve así:

╔═════════╦════════╦════════════╦═════════════════ ╦══════════════════╦════════════════════╗
║ TotalKB ║ FreeKB ║ TotalPages ║ TotalEmptyPages ║ BytesFreePercent ║ UnusedPagesPercent ║
╠═════════╬════════╬════════════╬═════════════════ ╬══════════════════╬════════════════════╣
║ 208 ║ 96 ║ 26 ║ 12 ║ 46.153800 ║ 46.153800 ║
╚═════════╩════════╩════════════╩═════════════════ ╩══════════════════╩════════════════════╝

Escribí una publicación de blog describiendo la función aquí .

En su escenario, ya que ha ejecutado ALTER TABLE ... REBUILD, debería ver un número muy bajo para TotalEmptyPages, pero supongo que todavía tendrá alrededor del 72% BytesFreePercent.

He usado tu CREATE TABLEscript para intentar recrear tu escenario.

Este es el MCVE que estoy usando:

DROP TABLE IF EXISTS dbo.MyTable;

CREATE TABLE [dbo].[MyTable](
    [Column1]  [int]            NOT NULL IDENTITY(1,1),
    [Column2]  [int]            NOT NULL,
    [Column3]  [int]            NOT NULL,
    [Column4]  [bit]            NOT NULL,
    [Column5]  [tinyint]        NOT NULL,
    [Column6]  [datetime]       NULL,
    [Column7]  [int]            NOT NULL,
    [Column8]  [varchar](100)   NULL,
    [Column9]  [varchar](256)   NULL,
    [Column10] [int]            NULL,
    [Column11] [image]          NULL,
    [Column12] [text]           NULL,
    [Column13] [varchar](100)   NULL,
    [Column14] [varchar](6)     NULL,
    [Column15] [int]            NOT NULL,
    [Column16] [bit]            NOT NULL,
    [Column17] [datetime]       NULL,
    [Column18] [varchar](50)    NULL,
    [Column19] [varchar](50)    NULL,
    [Column20] [varchar](60)    NULL,
    [Column21] [varchar](20)    NULL,
    [Column22] [varchar](120)   NULL,
    [Column23] [varchar](4)     NULL,
    [Column24] [varchar](75)    NULL,
    [Column25] [char](1)        NULL,
    [Column26] [varchar](50)    NULL,
    [Column27] [varchar](128)   NULL,
    [Column28] [varchar](50)    NULL,
    [Column29] [int]            NULL,
    [Column30] [text]           NULL,
 CONSTRAINT [PK] PRIMARY KEY CLUSTERED 
(
    [Column1] ASC,
    [Column2] ASC,
    [Column3] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column4]  DEFAULT (0) FOR [Column4]

ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column5]  DEFAULT (0) FOR [Column5]

ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column15]  DEFAULT (0) FOR [Column15]

ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column16]  DEFAULT (0) FOR [Column16]
GO

INSERT INTO dbo.MyTable (
      Column2
    , Column3
    , Column4
    , Column5
    , Column6
    , Column7
    , Column8
    , Column9
    , Column10
    , Column11
    , Column12
    , Column13
    , Column14
    , Column15
    , Column16
    , Column17
    , Column18
    , Column19
    , Column20
    , Column21
    , Column22
    , Column23
    , Column24
    , Column25
    , Column26
    , Column27
    , Column28
    , Column29
    , Column30
)
VALUES (
          0
        , 0
        , 0
        , 0
        , '2019-07-09 00:00:00'
        , 1
        , REPLICATE('A', 50)    
        , REPLICATE('B', 128)   
        , 0
        , REPLICATE(CONVERT(varchar(max), 'a'), 1)
        , REPLICATE(CONVERT(varchar(max), 'b'), 9000)
        , REPLICATE('C', 50)    
        , REPLICATE('D', 3)     
        , 0
        , 0
        , '2019-07-10 00:00:00'
        , REPLICATE('E', 25)    
        , REPLICATE('F', 25)    
        , REPLICATE('G', 30)    
        , REPLICATE('H', 10)    
        , REPLICATE('I', 120)   
        , REPLICATE('J', 4)     
        , REPLICATE('K', 75)    
        , 'L'       
        , REPLICATE('M', 50)    
        , REPLICATE('N', 128)   
        , REPLICATE('O', 50)    
        , 0
        , REPLICATE(CONVERT(varchar(max), 'c'), 90000)
);
--GO 100

;WITH dpa AS 
(
    SELECT dpa.*
        , page_free_space_percent_corrected = 
          CASE COALESCE(dpa.page_type_desc, N'')
            WHEN N'TEXT_MIX_PAGE' THEN 100 - COALESCE(dpa.page_free_space_percent, 100)
            WHEN N'TEXT_TREE_PAGE' THEN 100 - COALESCE(dpa.page_free_space_percent, 100)
            ELSE COALESCE(dpa.page_free_space_percent, 100)
          END
    FROM sys.dm_db_database_page_allocations(DB_ID(), OBJECT_ID('dbo.MyTable'), NULL, NULL, 'DETAILED') dpa
)
, src AS
(
SELECT TotalKB = COUNT_BIG(1) * 8192 / 1024
    , FreeKB = SUM((dpa.page_free_space_percent_corrected / 100) * CONVERT(bigint, 8192)) / 1024
    , TotalPages = COUNT_BIG(1)
    , TotalEmptyPages = SUM(CASE WHEN dpa.page_free_space_percent_corrected = 100 THEN 1 ELSE 0 END) --completely empty pages
FROM dpa
)
SELECT *
    , BytesFreePercent = (CONVERT(decimal(38,2), src.FreeKB) / src.TotalKB) * 100
    , UnusedPagesPercent = (CONVERT(decimal(38,2), src.TotalEmptyPages) / src.TotalPages) * 100
FROM src

La siguiente consulta muestra una sola línea para cada página asignada a la tabla y utiliza el mismo DMV no documentado:

SELECT DatabaseName = d.name
    , ObjectName = o.name
    , IndexName = i.name
    , PartitionID = dpa.partition_id
    , dpa.allocation_unit_type_desc
    , dpa.allocated_page_file_id
    , dpa.allocated_page_page_id
    , dpa.is_allocated
    , dpa.page_free_space_percent --this seems unreliable
    , page_free_space_percent_corrected = 
        CASE COALESCE(dpa.page_type_desc, N'')
        WHEN N'TEXT_MIX_PAGE' THEN 100 - COALESCE(dpa.page_free_space_percent, 100)
        WHEN N'TEXT_TREE_PAGE' THEN 100 - COALESCE(dpa.page_free_space_percent, 100)
        ELSE COALESCE(dpa.page_free_space_percent, 100)
        END
    , dpa.page_type_desc
    , dpa.is_page_compressed
    , dpa.has_ghost_records
FROM sys.dm_db_database_page_allocations(DB_ID(), OBJECT_ID('dbo.MyTable'), NULL, NULL, 'DETAILED') dpa
    LEFT JOIN sys.databases d ON dpa.database_id = d.database_id
    LEFT JOIN sys.objects o ON dpa.object_id = o.object_id
    LEFT JOIN sys.indexes i ON dpa.object_id = i.object_id AND dpa.index_id = i.index_id
WHERE dpa.database_id = DB_ID() --sanity check for sys.objects and sys.indexes

El resultado mostrará muchas filas si lo ejecuta contra su tabla real en su entorno de prueba, pero puede permitirle ver dónde está el problema.

¿Puedes ejecutar el siguiente script y publicar los resultados en tu pregunta? Solo trato de asegurarme de que estamos en la misma página.

SELECT ObjectName = s.name + N'.' + o.name
    , ReservedPageCount = SUM(dps.reserved_page_count)
    , UsePageCount = SUM(dps.used_page_count)
FROM sys.schemas s
    INNER JOIN sys.objects o ON s.schema_id = o.schema_id
    INNER JOIN sys.partitions p ON o.object_id = p.object_id
    INNER JOIN sys.dm_db_partition_stats dps ON p.object_id = dps.object_id
WHERE s.name = N'dbo'
    AND o.name = N'MyTable'
GROUP BY s.name + N'.' + o.name;

2
Running DBCC UPDATEUSAGEactualizó el espacio no utilizado y el número de páginas no utilizadas. Parece que el uso del disco y la información de la página informada por SQL Server estaba extremadamente fuera de sincronización: actualicé mi publicación con los detalles. Tengo curiosidad por saber cómo habría sucedido esto en primer lugar, pero al menos se encontró el problema. Gracias por toda su ayuda, ¡realmente lo aprecio!
Ken

0

Una de las columnas es una imagen de tipo LOB, y almacena archivos que varían en tamaño desde unos pocos KB hasta varios cientos de MB

Podría estar experimentando fragmentación interna.
¿Cuál es la fragmentación de la página para esta tabla?
¿Y es la fragmentación para la fila diferente de las páginas fuera de fila?

Dices que tienes archivos de unos pocos KB.
SQL Server almacena todo en páginas de 8060 bytes. Es decir, si tiene una fila (o datos fuera de fila) de 4040 bytes y la siguiente es similar, no puede caber en la misma página y desperdiciará la mitad de su espacio. Intente cambiar el tamaño de su fila almacenando columnas de longitud variable (comience con la imagen, por ejemplo) en una tabla diferente.


No creo que la fragmentación sea el problema. Después de reconstruir los índices, la fragmentación del índice agrupado es del 0,45% y la plenitud de la página es del 98,93%.
Ken

Reconstruir una tabla o índice no ayudará si sufre de filas muy grandes o datos LOB que no se ajustan bien a las páginas de 8 KB. Es lo que Max Vernon explicó con más detalle: "tienes muchas páginas parcialmente vacías". también llamado fragmentación interna
DrTrunks Bell

-3

¿La base de datos está en modo de recuperación completa? Si es así, cuando realiza una reducción, está registrando todos los cambios y no lo reducirá de la manera esperada. Dependiendo de sus horas de operación, puede realizar una copia de seguridad, cambiar al modo de recuperación de envío masivo y luego ejecutar la reducción en el archivo de datos. Después de eso, querrás ejecutar scripts de índice para reparar / reconstruir y volver a la recuperación completa. Eso es lo que intentaría de todos modos, pero de nuevo, depende de sus horas de operación para todo esto.


44
Mencionar el modelo de recuperación es interesante. Creo que sería más aplicable si el OP tuviera problemas con el tamaño de su archivo de registro. Tal como están las cosas, están teniendo problemas con el tamaño del archivo de datos, por lo que me sorprendería si el modelo de recuperación causara el problema descrito.
Josh Darnell

Es cierto, pero las únicas veces que ejecuté una reducción y realmente no impactó el espacio fue por el modelo de recuperación, así que pensé que valía la pena mencionarlo en caso de que fuera un diagnóstico erróneo.
John-Henry Lochbaum

-3

La única vez que no he podido reducir una base de datos y reclamar espacio es porque no se puede reducir una base de datos más allá del tamaño inicial de la base de datos cuando se creó. Entonces, por ejemplo, si su base de datos es una copia de la base de datos de producción y creó la base de datos por primera vez a 525 GB, el servidor sql no le permitirá reducir el tamaño por debajo de 525 GB sin importar la cantidad de datos que elimine de la base de datos. Pero si el DB se creó por debajo de 383 GB y luego creció a 525 GB, no debería tener problemas para recuperar el espacio. Durante mucho tiempo pensé que esta es una restricción estúpida y arbitraria por parte de Microsoft.

Reduzca la base de datos solo hasta su tamaño inicial que se establece después de crear la base de datos


La pregunta no es acerca de reducir una base de datos (y si lo fuera, la capacidad de reducirla depende del espacio utilizado después de la región de tamaño inicial)
eckes

Siempre que haya espacio sin usar, es posible reducir la base de datos a un par de MB independientemente del tamaño original. No es necesariamente una buena idea, pero he tenido muchas ocasiones para reducir las bases de datos y nunca toparme con un límite como este.
Ray

-3

Me he encontrado con este problema antes en las cajas de producción, lo que debe hacer es reconstruir tablas e índices para cada tabla (en ese orden).

Aquí está la consulta que uso para mantener las tablas bajo control. Le ayudará a determinar qué tablas deben reconstruirse y a crear las consultas SQL que necesita ejecutar. Esta consulta se limita a aquellos con más de 1 MB de espacio sin usar y una proporción de 5% sin usar, por lo que solo reconstruye lo que realmente necesita para centrarse:

SELECT  'alter table [' + t.NAME + '] rebuild;' AS SQL1, 'alter index all on [' + t.NAME + '] rebuild;' as SQL2, t.NAME AS TableName, p.rows AS RowCounts, SUM(a.total_pages) * 8/1024 AS TotalSpaceMB,  SUM(a.used_pages) * 8/1024 AS UsedSpaceMB,  (SUM(a.total_pages) - SUM(a.used_pages)) * 8/1024 AS UnusedSpaceMB, case when SUM(a.total_pages)=0 then 0 else (SUM(a.total_pages) - SUM(a.used_pages))*100/SUM(a.total_pages) end as Ratio  FROM     sys.tables t (nolock) INNER JOIN       sys.indexes i (nolock)  ON t.OBJECT_ID = i.object_id INNER JOIN  sys.partitions p (nolock) ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id INNER JOIN  sys.allocation_units a (nolock) ON p.partition_id = a.container_id LEFT OUTER JOIN  sys.schemas s (nolock) ON t.schema_id = s.schema_id WHERE  t.is_ms_shipped = 0 AND i.OBJECT_ID > 255  GROUP BY  t.Name, s.Name, p.Rows  
having  (SUM(a.total_pages) - SUM(a.used_pages)) * 8/1024>1
and (SUM(a.total_pages) - SUM(a.used_pages))*100/SUM(a.total_pages)>5
ORDER BY    5 desc

reconstruir la tabla, como el OP dice que han hecho, eliminaría la mayor parte de la fragmentación. Dudo que hacer otra reconstrucción ayude más.
Max Vernon el
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.