Menambahkan SPARSE membuat tabel lebih besar

9

Saya memiliki tabel log generik, sekitar 5m baris.
Ada bidang "sangat diketik" yang menyimpan jenis acara, dan sekelompok kolom "diketik dengan salah" yang berisi data yang relevan dengan acara tersebut. Artinya, arti kolom-kolom "diketik dengan keliru" tergantung pada jenis acara.

Kolom-kolom ini didefinisikan sebagai:

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

Kolom 1 dan 2 di masing-masing jenis banyak digunakan, tetapi mulai dari nomor 3, sangat sedikit jenis acara yang menyediakan informasi sebanyak ini. Karena itu saya memutuskan untuk menandai kolom 3-5 di setiap jenis sebagai SPARSE.

Saya melakukan beberapa analisis terlebih dahulu, dan melihat bahwa, memang, setidaknya 80% dari data di masing-masing kolom adalah null, dan di sekitar 100% dari data null. Menurut tabel ambang penghematan 40% , SPARSEakan menjadi kemenangan besar bagi mereka.

Jadi saya pergi dan melamar SPARSEkolom 3-5 di setiap kelompok. Sekarang meja saya membutuhkan sekitar 1.8Gb dalam ruang data seperti yang dilaporkan oleh sp_spaceused, sedangkan sebelum sparsing adalah 1Gb.

Saya mencoba dbcc cleantable, tetapi tidak berpengaruh.
Lalu dbcc shrinkdatabase, tidak ada efek juga.

Bingung, saya menghapus SPARSEdan mengulangi dbcc. Ukuran tabel tetap di 1.8Gb.

Apa yang menyebabkannya?

GSerg
sumber
Akan mencoba dan mereproduksi. Hanya memetikannya membuat perbedaan adalah tabel tumpukan atau apakah itu memiliki indeks berkerumun?
Martin Smith
@ MartinSmith memiliki indeks berkerumun rowid int not null identity(1,1) primary key clustered.
GSerg

Jawaban:

14

Anda perlu membangun kembali indeks berkerumun setelah membuat kolom jarang. Kolom yang dijatuhkan masih ada di halaman data hingga Anda melakukan ini seperti yang dapat dilihat dengan kueri yang menentang sys.system_internals_partition_columnsatau menggunakanDBCC 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
sumber
1
Luar biasa. Haruskah kita menganggapnya sebagai bug dalam dokumentasi ? "Mesin Database SQL Server menggunakan prosedur berikut untuk melakukan perubahan ini: 1) Menambahkan kolom baru ke tabel dalam ukuran dan format penyimpanan baru. 2) Untuk setiap baris dalam tabel, perbarui dan salin nilai yang disimpan dalam yang lama kolom ke kolom baru. 3) Menghapus kolom lama dari skema tabel. 4) Membangun kembali tabel untuk mendapatkan kembali ruang yang digunakan oleh kolom lama. "
GSerg
3
@ GSerg - Ah benar. Setuju sepertinya poin 4 tidak benar. Mengingat Anda melakukan ini untuk 12 kolom maka Anda tidak ingin membangun kembali terjadi secara implisit untuk setiap kolom meskipun begitu kelakuannya benar tetapi tidak pada dokumentasi.
Martin Smith
1
@SQLKiwi - Terima kasih. Selesai
Martin Smith