Agregar SPARSE hizo la mesa mucho más grande


9

Tengo una tabla de registro genérico, aproximadamente 5 millones de filas.
Hay un campo "fuertemente tipado" que almacena el tipo de evento, y un montón de columnas "tipadas de forma incorrecta" que contienen datos relevantes para el evento. Es decir, el significado de esas columnas "mal escritas" depende del tipo de evento.

Estas columnas se definen como:

USER_CHAR1 nvarchar(150) null,
USER_CHAR2 nvarchar(150) null,
USER_CHAR3 nvarchar(150) null,
USER_CHAR4 nvarchar(150) null,
USER_CHAR5 nvarchar(150) null,

USER_INTEGER1 int null,
USER_INTEGER2 int null,
USER_INTEGER3 int null,
USER_INTEGER4 int null,
USER_INTEGER5 int null,

USER_FLAG1 bit null,
USER_FLAG2 bit null,
USER_FLAG3 bit null,
USER_FLAG4 bit null,
USER_FLAG5 bit null,

USER_FLOAT1 float null,
USER_FLOAT2 float null,
USER_FLOAT3 float null,
USER_FLOAT4 float null,
USER_FLOAT5 float null

Las columnas 1 y 2 en cada tipo se usan mucho, pero a partir del número 3, muy pocos tipos de eventos proporcionarían tanta información. Por lo tanto, decidí marcar las columnas 3-5 en cada tipo como SPARSE.

Primero hice un análisis, y vi que, de hecho, al menos el 80% de los datos en cada una de esas columnas es null, y en aproximadamente el 100% de los datos null. Según la tabla de umbral de ahorro del 40% , SPARSEsería una gran victoria para ellos.

Así que fui y apliqué SPARSEa las columnas 3-5 en cada grupo. Ahora mi tabla ocupa aproximadamente 1.8 Gb en espacio de datos según lo informado por sp_spaceused, mientras que antes de ahorrar era de 1 Gb.

Lo intenté dbcc cleantable, pero no tuvo ningún efecto.
Entonces dbcc shrinkdatabase, tampoco tiene efecto.

Perplejo, me quité SPARSEy repetí el dbccs. El tamaño de la mesa se mantuvo en 1.8Gb.

¿Lo que da?


Lo intentaremos y reproduciremos. Solo en caso de que haga alguna diferencia, ¿la tabla es un montón o tiene un índice agrupado?
Martin Smith

@MartinSmith tiene un índice agrupado rowid int not null identity(1,1) primary key clustered.
GSerg

Respuestas:


14

Debe reconstruir el índice agrupado después de hacer dispersas las columnas. Las columnas descartadas aún existen en la página de datos hasta que haga esto, como se puede ver con una consulta en contra sys.system_internals_partition_columnso usandoDBCC PAGE

SET NOCOUNT ON;
CREATE TABLE Thing 
(
ThingId int IDENTITY CONSTRAINT PK PRIMARY KEY,
USER_CHAR1 nvarchar(150) null,
USER_CHAR2 nvarchar(150) null,
USER_CHAR3 nvarchar(150) null,
USER_CHAR4 nvarchar(150) null,
USER_CHAR5 nvarchar(150) null
)
INSERT INTO Thing
SELECT REPLICATE('A',150),
       CASE WHEN number % 5 = 1 THEN REPLICATE('A',150) END,
       CASE WHEN number % 5 = 2 THEN REPLICATE('A',150) END,
       CASE WHEN number % 5 = 3 THEN REPLICATE('A',150) END,              
       CASE WHEN number % 5 = 4 THEN REPLICATE('A',150) END
FROM master..spt_values   

EXEC sp_spaceused 'Thing'

ALTER TABLE dbo.Thing ALTER COLUMN USER_CHAR2 ADD SPARSE
ALTER TABLE dbo.Thing ALTER COLUMN USER_CHAR3 ADD SPARSE
ALTER TABLE dbo.Thing ALTER COLUMN USER_CHAR4 ADD SPARSE
ALTER TABLE dbo.Thing ALTER COLUMN USER_CHAR5 ADD SPARSE

DECLARE @DynSQL NVARCHAR(MAX);

SELECT @DynSQL =  'DBCC TRACEON (3604);
                   DBCC PAGE(0, ' + LEFT(file_id,10) + ', ' + LEFT(page_id,10) + ', 3); 
                   DBCC TRACEOFF(3604); ' 
FROM Thing
CROSS APPLY sys.fn_PhysLocCracker(%%physloc%%)
WHERE ThingId=76

EXEC(@DynSQL)    

SELECT pc.*
FROM sys.system_internals_partition_columns pc
JOIN sys.partitions p on p.partition_id=pc.partition_id
WHERE p.object_id = object_id('Thing')
AND pc.is_dropped=1

 EXEC sp_spaceused 'Thing'

ALTER INDEX PK ON Thing REBUILD;    

SELECT @DynSQL =  'DBCC TRACEON (3604);
                   DBCC PAGE(0, ' + LEFT(file_id,10) + ', ' + LEFT(page_id,10) + ', 3); 
                   DBCC TRACEOFF(3604); ' 
FROM Thing
CROSS APPLY sys.fn_PhysLocCracker(%%physloc%%)
WHERE ThingId=76

EXEC(@DynSQL)    

SELECT pc.*
FROM sys.system_internals_partition_columns pc
JOIN sys.partitions p on p.partition_id=pc.partition_id
WHERE p.object_id = object_id('Thing')
AND pc.is_dropped=1

EXEC sp_spaceused 'Thing'

DROP TABLE Thing 

1
Increíble. ¿Deberíamos tomarlo como un error en la documentación ? "El Motor de base de datos de SQL Server utiliza el siguiente procedimiento para lograr este cambio: 1) Agrega una nueva columna a la tabla en el nuevo tamaño y formato de almacenamiento. 2) Para cada fila de la tabla, actualiza y copia el valor almacenado en el antiguo columna a la nueva columna. 3) Elimina la columna anterior del esquema de la tabla. 4) Reconstruye la tabla para recuperar el espacio utilizado por la columna anterior. "
GSerg

3
@GSerg - Ah, claro. De acuerdo parece que el punto 4 no es correcto entonces. Dado que está haciendo esto para 12 columnas, entonces no querría que la reconstrucción ocurriera implícitamente para cada columna, aunque parece que el comportamiento es correcto pero no la documentación.
Martin Smith

1
@SQLKiwi - Gracias. Hecho
Martin Smith
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.