Dodanie SPARSE sprawiło, że stół stał się znacznie większy

9

Mam ogólną tabelę dziennika, około 5m wierszy.
Istnieje pole „silnie typowane”, które przechowuje typ zdarzenia, oraz kilka kolumn „luźno typowanych”, które zawierają dane istotne dla zdarzenia. Oznacza to, że znaczenie tych „luźno wpisanych” kolumn zależy od typu zdarzenia.

Te kolumny są zdefiniowane jako:

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

Kolumny 1 i 2 w każdym typie są intensywnie używane, ale począwszy od liczby 3, bardzo niewiele typów zdarzeń zapewniłoby tyle informacji. Dlatego postanowiłem oznaczyć kolumny 3-5 w każdym typie jako SPARSE.

Najpierw dokonałem analizy i zobaczyłem, że rzeczywiście co najmniej 80% danych w każdej z tych kolumn jest null, a około 100% danych null. Według 40% Tabela progowej oszczędności , SPARSEbyłby to ogromny wygrana na nich.

Poszedłem i zastosowałem się SPARSEdo kolumn 3-5 w każdej grupie. Teraz moja tabela zajmuje około 1,8 Gb w przestrzeni danych, jak donosi sp_spaceused, podczas gdy przed sparowaniem była to 1 Gb.

Próbowałem dbcc cleantable, ale nie przyniosło to efektu.
Wtedy też dbcc shrinkdatabasenie ma żadnego efektu.

Zdziwiony usunąłem SPARSEi powtórzyłem dbccs. Rozmiar tabeli pozostał na poziomie 1,8 Gb.

Co daje?

GSerg
źródło
Spróbuje się odtworzyć. Wystarczy dodać, że robi to różnicę, czy tabela jest stertą, czy ma indeks klastrowany?
Martin Smith
@MartinSmith Ma indeks klastrowany rowid int not null identity(1,1) primary key clustered.
GSerg

Odpowiedzi:

14

Musisz odbudować indeks klastrowany po zmniejszeniu liczby kolumn. Upuszczone kolumny nadal istnieją na stronie danych, dopóki nie zrobisz tego, co można zobaczyć po zapytaniu przeciwko sys.system_internals_partition_columnslub użyciuDBCC 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
źródło
1
Niesamowite. Czy powinniśmy uznać to za błąd w dokumentacji ? „Aparat baz danych programu SQL Server stosuje następującą procedurę w celu wprowadzenia tej zmiany: 1) Dodaje nową kolumnę do tabeli w nowym rozmiarze i formacie. 2) Dla każdego wiersza w tabeli aktualizuje i kopiuje wartość zapisaną w starym kolumna do nowej kolumny. 3) Usuwa starą kolumnę ze schematu tabeli. 4) Odbudowuje tabelę, aby odzyskać miejsce
zajęte
3
@GSerg - Ach racja. Zgadzam się, że punkt 4 nie jest wtedy poprawny. Biorąc pod uwagę, że robisz to dla 12 kolumn, nie chciałbyś, aby odbudowanie odbywało się niejawnie dla każdej kolumny, chociaż wydaje się, że zachowanie jest prawidłowe, ale nie dokumentacja.
Martin Smith
1
@SQLKiwi - Dzięki. Sporządzono
Martin Smith,