Ajouter une table SPARSE beaucoup plus grande

9

J'ai une table de log générique, environ 5m de lignes.
Il y a un champ "fortement typé" qui stocke le type d'événement, et un tas de colonnes "losely typées" qui contiennent des données pertinentes pour l'événement. C'est-à-dire que la signification de ces colonnes "typées losely" dépend du type de l'événement.

Ces colonnes sont définies comme:

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

Les colonnes 1 et 2 de chaque type sont largement utilisées, mais à partir du numéro 3, très peu de types d'événements fourniraient autant d'informations. J'ai donc souhaité marquer les colonnes 3 à 5 de chaque type comme SPARSE.

J'ai d'abord fait une analyse et j'ai vu qu'en effet, au moins 80% des données dans chacune de ces colonnes le sont null, et dans environ 100% des données null. Selon le tableau du seuil d'économie de 40% , ce SPARSEserait une énorme victoire pour eux.

Je suis donc allé et appliqué SPARSEaux colonnes 3-5 dans chaque groupe. Maintenant, ma table prend environ 1,8 Go dans l'espace de données, comme indiqué par sp_spaceused, alors qu'avant d'économiser, elle était de 1 Go.

J'ai essayé dbcc cleantable, mais cela n'a eu aucun effet.
Ensuite dbcc shrinkdatabase, aucun effet non plus.

Intrigué, j'ai retiré SPARSEet répété le par dbcc. La taille de la table est restée à 1,8 Go.

Ce qui donne?

GSerg
la source
Va essayer de se reproduire. Au cas où cela ferait une différence, la table est-elle un tas ou a-t-elle un index cluster?
Martin Smith
@MartinSmith Possède un index clusterisé rowid int not null identity(1,1) primary key clustered.
GSerg

Réponses:

14

Vous devez reconstruire l'index cluster après avoir rendu les colonnes éparses. Les colonnes supprimées existent toujours dans la page de données jusqu'à ce que vous le fassiez comme vous pouvez le voir avec une requête sur sys.system_internals_partition_columnsou en utilisantDBCC 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
la source
1
Impressionnant. Faut-il le prendre comme un bug dans la documentation ? «Le moteur de base de données SQL Server utilise la procédure suivante pour effectuer cette modification: 1) Ajoute une nouvelle colonne à la table dans la nouvelle taille et le nouveau format de stockage. 2) Pour chaque ligne de la table, met à jour et copie la valeur stockée dans l'ancienne colonne à la nouvelle colonne. 3) Supprime l'ancienne colonne du schéma de table. 4) Reconstruit la table pour récupérer l'espace utilisé par l'ancienne colonne. "
GSerg
3
@GSerg - Ah oui. D'accord, le point 4 n'est pas correct alors. Étant donné que vous faites cela pour 12 colonnes, vous ne voudriez pas que la reconstruction se produise implicitement pour chaque colonne, mais le comportement semble correct, mais pas la documentation.
Martin Smith
1
@SQLKiwi - Merci. Fait
Martin Smith, le