Додавання 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і повторив dbccs. Розмір столу залишився на рівні 1,8 Гб.

Що дає?


Буде спробувати і відтворити. Просто у випадку, якщо це має значення, це таблиця купи або вона має кластерний індекс?
— Мартін Сміт

@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 використовує наступну процедуру для здійснення цієї зміни: 1) додає новий стовпець до таблиці в новому розмірі та форматі зберігання. 2) для кожного рядка таблиці оновлення та копіювання значення, збереженого в старому 3) Вилучає старий стовпець зі схеми таблиці. 4) Перебудовує таблицю, щоб повернути простір, використаний старим стовпцем. "
— GSerg

3
@GSerg - Ага. Погодьтеся, здається, точка 4 тоді не є правильною. Зважаючи на те, що ви робите це для 12 стовпців, тоді ви не хочете, щоб відбудова відбувалася неявно для кожного стовпця, хоча так здається, що поведінка правильна, але не документація.
— Мартін Сміт

1
@SQLKiwi - Дякую Готово
— Мартін Сміт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.