Durch Hinzufügen von SPARSE wurde der Tisch viel größer


9

Ich habe eine generische Protokolltabelle, ungefähr 5 Millionen Zeilen.
Es gibt ein "stark typisiertes" Feld, in dem der Ereignistyp gespeichert ist, und eine Reihe von "lose typisierten" Spalten, die für das Ereignis relevante Daten enthalten. Das heißt, die Bedeutung dieser "lose typisierten" Spalten hängt von der Art des Ereignisses ab.

Diese Spalten sind definiert als:

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

Die Spalten 1 und 2 in jedem Typ werden häufig verwendet, aber ab Nummer 3 würden nur sehr wenige Ereignistypen so viele Informationen liefern. Ich habe mich daher entschlossen, die Spalten 3-5 in jedem Typ als zu markieren SPARSE.

Ich habe zuerst eine Analyse durchgeführt und festgestellt, dass tatsächlich mindestens 80% der Daten in jeder dieser Spalten nullund in etwa 100% der Daten vorhanden sind null. Nach der 40% Einsparung Schwellenwerttabelle , SPARSEwäre ein großer Gewinn für sie sein.

Also habe ich mich SPARSEin jeder Gruppe für die Spalten 3-5 beworben . Jetzt nimmt meine Tabelle ungefähr 1,8 GB Datenraum ein, wie von angegeben sp_spaceused, während sie vor dem Sparsing 1 GB betrug.

Ich habe es versucht dbcc cleantable, aber es hatte keine Wirkung.
Dann dbcc shrinkdatabaseauch keine Wirkung.

Verwirrt entfernte ich SPARSEdas dbccs und wiederholte es . Die Größe der Tabelle blieb bei 1,8 GB.

Was gibt?


Wird versuchen zu reproduzieren. Nur für den Fall, dass es einen Unterschied macht, ist die Tabelle ein Heap oder hat sie einen Clustered-Index?
Martin Smith

@MartinSmith Hat einen Clustered-Index rowid int not null identity(1,1) primary key clustered.
GSerg

Antworten:


14

Sie müssen den Clustered-Index neu erstellen, nachdem Sie die Spalten spärlich gemacht haben. Die abgelegten Spalten sind auf der Datenseite noch vorhanden, bis Sie dies tun, wie bei einer Abfrage gegen sys.system_internals_partition_columnsoder unter Verwendung von angezeigt wirdDBCC 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
Genial. Sollten wir es als Fehler in der Dokumentation betrachten ? "Das SQL Server-Datenbankmodul verwendet das folgende Verfahren, um diese Änderung durchzuführen: 1) Fügt der Tabelle eine neue Spalte in der neuen Speichergröße und im neuen Format hinzu. 2) Aktualisiert und kopiert für jede Zeile in der Tabelle den in der alten gespeicherten Wert Spalte in die neue Spalte. 3) Entfernt die alte Spalte aus dem Tabellenschema. 4) Erstellt die Tabelle neu, um den von der alten Spalte verwendeten Speicherplatz zurückzugewinnen. "
GSerg

3
@ GSerg - Ah richtig. Einverstanden scheint Punkt 4 dann nicht richtig zu sein. Wenn Sie dies für 12 Spalten tun, möchten Sie nicht, dass die Neuerstellung implizit für jede Spalte erfolgt. Das Verhalten scheint also korrekt zu sein, nicht jedoch die Dokumentation.
Martin Smith

1
@ SQLKiwi - Danke. Fertig
Martin Smith
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.