Adicionar SPARSE tornou a tabela muito maior

9

Eu tenho uma tabela de log genérica, com cerca de 5m de linhas.
Há um campo "fortemente tipado" que armazena o tipo de evento e várias colunas "mal tipadas" que contêm dados relevantes para o evento. Ou seja, o significado dessas colunas "com tipagem incorreta" depende do tipo do evento.

Essas colunas são definidas 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

As colunas 1 e 2 em cada tipo são muito usadas, mas a partir do número 3, muito poucos tipos de eventos forneceriam tanta informação. Por isso, desejei marcar as colunas 3-5 em cada tipo como SPARSE.

Fiz algumas análises primeiro e vi que, de fato, pelo menos 80% dos dados em cada uma dessas colunas são nulle em cerca de 100% dos dados null. De acordo com a tabela de limiar de economia de 40% , SPARSEseria uma grande vitória para eles.

Então, fui e me inscrevi SPARSEnas colunas 3-5 de cada grupo. Agora, minha tabela ocupa cerca de 1,8 Gb no espaço de dados, conforme relatado por sp_spaceused, enquanto antes era poupado 1 Gb.

Eu tentei dbcc cleantable, mas não teve efeito.
Então dbcc shrinkdatabase, também não tem efeito.

Intrigado, retirei SPARSEe repeti os dbccs. O tamanho da tabela permaneceu em 1,8 Gb.

O que da?

GSerg
fonte
Tentará se reproduzir. Apenas caso isso faça alguma diferença, a tabela é um heap ou possui um índice em cluster?
Martin Smith
@ MartinSmith Possui um índice em cluster rowid int not null identity(1,1) primary key clustered.
GSerg 04/02/12

Respostas:

14

Você precisa recriar o índice em cluster depois de tornar as colunas esparsas. As colunas descartadas ainda existem na página de dados até você fazer isso como pode ser visto com uma consulta sys.system_internals_partition_columnsou 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 
Martin Smith
fonte
11
Impressionante. Devemos tomá-lo como um bug na documentação ? "O Mecanismo de Banco de Dados do SQL Server usa o seguinte procedimento para realizar essa alteração: 1) Adiciona uma nova coluna à tabela no novo tamanho e formato de armazenamento. 2) Para cada linha da tabela, atualiza e copia o valor armazenado na antiga coluna à nova coluna. 3) Remove a coluna antiga do esquema da tabela. 4) Reconstrói a tabela para recuperar o espaço usado pela coluna antiga. "
GSerg
3
@GSerg - Ah, certo. Concordo parece que o ponto 4 não está correto então. Como você está fazendo isso para 12 colunas, não deseja que a reconstrução ocorra implicitamente para cada coluna, embora pareça que o comportamento esteja correto, mas não a documentação.
Martin Smith
11
@SQLKiwi - Obrigado. Concluído
Martin Smith