Добавление SPARSE сделало таблицу намного больше

9

У меня есть общая таблица журнала, около 5 м строк.
Есть «строго типизированное» поле, в котором хранится тип события, и набор «ошибочно типизированных» столбцов, которые содержат данные, относящиеся к событию. То есть значение этих «плохо напечатанных» столбцов зависит от типа события.

Эти столбцы определены как:

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

Столбцы 1 и 2 в каждом типе используются интенсивно, но, начиная с номера 3, очень немногие типы событий могут предоставить такую ​​большую информацию. Поэтому я решил пометить столбцы 3-5 в каждом типе как SPARSE.

Сначала я провел некоторый анализ и увидел, что, по крайней мере, 80% данных в каждом из этих столбцов есть null, а в некоторых - около 100% null. В соответствии с пороговой таблицей сбережений 40% , SPARSEэто будет огромной победой для них.

Поэтому я пошел и применил SPARSEк столбцам 3-5 в каждой группе. Теперь моя таблица занимает около 1,8 ГБ в пространстве данных, как сообщалось sp_spaceused, тогда как до разбора она была 1 ГБ.

Я пытался dbcc cleantable, но это не имело никакого эффекта.
Тогда dbcc shrinkdatabaseтоже никакого эффекта.

Озадаченный, я удалил SPARSEи повторил dbccс. Размер таблицы остался на уровне 1,8 Гб.

Что дает?

GSerg
источник
Постараюсь и воспроизведу. Просто если есть какая-то разница, является ли таблица кучей или она имеет кластерный индекс?
Мартин Смит
@MartinSmith Имеет кластерный индекс rowid int not null identity(1,1) primary key clustered.
GSerg

Ответы:

14

Вам нужно перестроить кластерный индекс после того, как столбцы будут редкими. Пропущенные столбцы все еще существуют на странице данных, пока вы не сделаете это, как можно увидеть с помощью запроса sys.system_internals_partition_columnsили использованияDBCC 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
Потрясающие. Должны ли мы принять это как ошибку в документации ? «SQL Server Database Engine использует следующую процедуру для выполнения этого изменения: 1) Добавляет новый столбец в таблицу в новом размере и формате хранилища. 2) Для каждой строки в таблице обновляет и копирует значение, сохраненное в старом столбец к новому столбцу. 3) Удаляет старый столбец из схемы таблицы. 4) Перестраивает таблицу, чтобы освободить пространство, используемое старым столбцом. "
GSerg
3
@GSerg - Ах да. Согласитесь, кажется, пункт 4 не является правильным тогда. Учитывая, что вы делаете это для 12 столбцов, вы не хотели бы, чтобы перестройка происходила неявно для каждого столбца, хотя поведение кажется правильным, но не документация.
Мартин Смит
1
@SQLKiwi - Спасибо. Готово
Мартин Смит