L'aggiunta di SPARSE ha reso la tabella molto più grande


9

Ho una tabella di registro generica, circa 5m di righe.
C'è un campo "fortemente tipizzato" che memorizza il tipo di evento e un gruppo di colonne "tipicamente losco" che contengono dati rilevanti per l'evento. Cioè, il significato di quelle colonne "digitate male" dipende dal tipo di evento.

Queste colonne sono definite come:

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

Le colonne 1 e 2 in ciascun tipo sono molto utilizzate, ma a partire dal numero 3, pochissimi tipi di eventi fornirebbero così tante informazioni. Ho quindi voluto contrassegnare le colonne 3-5 in ciascun tipo come SPARSE.

Ho fatto prima alcune analisi e ho visto che, in effetti, almeno l'80% dei dati in ciascuna di quelle colonne lo è null, e in circa il 100% dei dati lo è null. Secondo la tabella della soglia di risparmio del 40% , SPARSEsarebbe una grande vittoria per loro.

Quindi sono andato e applicato SPARSEalle colonne 3-5 in ciascun gruppo. Ora la mia tabella occupa circa 1,8 GB di spazio dati come riportato da sp_spaceused, mentre prima di risparmiare era di 1 GB.

Ci ho provato dbcc cleantable, ma non ha avuto effetto.
Quindi dbcc shrinkdatabase, nessun effetto neanche.

Perplesso, ho rimosso SPARSEe ripetuto la dbccs. La dimensione della tabella è rimasta a 1,8 GB.

Cosa dà?


Proverà a riprodursi. In caso contrario, fa alcuna differenza la tabella è un heap o ha un indice cluster?
Martin Smith,

@MartinSmith Ha un indice cluster rowid int not null identity(1,1) primary key clustered.
GSerg

Risposte:


14

È necessario ricostruire l'indice cluster dopo aver ridotto le colonne. Le colonne rilasciate esistono ancora nella pagina dei dati fino a quando non si esegue questa operazione, come si può vedere con una query a confronto sys.system_internals_partition_columnso in usoDBCC 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
Eccezionale. Dovremmo prenderlo come un bug nella documentazione ? "Il motore di database di SQL Server utilizza la seguente procedura per eseguire questa modifica: 1) Aggiunge una nuova colonna alla tabella nella nuova dimensione e formato di archiviazione. 2) Per ogni riga nella tabella, aggiorna e copia il valore archiviato nella vecchia colonna alla nuova colonna. 3) Rimuove la vecchia colonna dallo schema della tabella. 4) Ricostruisce la tabella per recuperare lo spazio usato dalla vecchia colonna. "
GSerg

3
@GSerg - Ah giusto. D'accordo sembra che il punto 4 non sia corretto allora. Dato che lo stai facendo per 12 colonne, non vorrai che la ricostruzione avvenga implicitamente per ogni colonna, anche se sembra che il comportamento sia corretto ma non la documentazione.
Martin Smith,

1
@SQLKiwi - Grazie. Fatto
Martin Smith, il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.