Non ho potuto resistere a unirmi a Niko con un'altra risposta (benvenuto, Niko!). In generale, sono d'accordo con Niko sul fatto che le limitazioni della modalità batch in SQL 2012 (se Niko non si collegherà al suo blog, lo farò :)) potrebbero essere una delle principali preoccupazioni. Ma se riesci a convivere con quelli e hai il pieno controllo su ogni query che stai scrivendo sulla tabella per esaminarla attentamente, columnstore potrebbe funzionare per te in SQL 2012.
Per quanto riguarda le tue domande specifiche sulla colonna identità, ho scoperto che la colonna identità si comprime molto bene e consiglio vivamente di includerla nel tuo indice columnstore in qualsiasi test iniziale. (Nota che se la colonna identità è anche l'indice cluster del tuo b-tree, sarà automaticamente inclusa nel tuo indice columnstore non cluster .)
Per riferimento, ecco le dimensioni che ho osservato per ~ 10MM righe di dati della colonna identità. Il columnstore caricato per l'eliminazione ottimale del segmento viene compresso a 26 MB (rispetto a 113 MB per la PAGE
compressione della tabella del rowstore) e anche il columnstore creato su un b-tree ordinato casualmente è di soli 40 MB. Questo dimostra un enorme vantaggio in termini di compressione, anche rispetto alla migliore compressione b-tree che SQL ha da offrire e anche se non ti preoccupi di allineare i tuoi dati per l'eliminazione ottimale del segmento (cosa che faresti prima creando un b-tree e poi costruendo il tuo columnstore con MAXDOP
1).
Ecco lo script completo che ho usato nel caso in cui desideri giocare:
-- Confirm SQL version
SELECT @@version
--Microsoft SQL Server 2012 - 11.0.5613.0 (X64)
-- May 4 2015 19:05:02
-- Copyright (c) Microsoft Corporation
-- Enterprise Edition: Core-based Licensing (64-bit) on Windows NT 6.3 <X64> (Build 9600: )
-- Create a columnstore table with identity column that is the primary key
-- This will yield 10 columnstore segments @ 1048576 rows each
SELECT i = IDENTITY(int, 1, 1), ROW_NUMBER() OVER (ORDER BY randGuid) as randCol
INTO #testIdentityCompression_sortedColumnstore
FROM (
SELECT TOP 10485760 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS randI, NEWID() AS randGuid
FROM master..spt_values v1
CROSS JOIN master..spt_values v2
CROSS JOIN master..spt_values v3
) r
ORDER BY r.randI
GO
ALTER TABLE #testIdentityCompression_sortedColumnstore
ADD PRIMARY KEY (i)
GO
-- Load using a pre-ordered b-tree and one thread for optimal segment elimination
-- See http://www.nikoport.com/2014/04/16/clustered-columnstore-indexes-part-29-data-loading-for-better-segment-elimination/
CREATE NONCLUSTERED COLUMNSTORE INDEX cs_#testIdentityCompression_sortedColumnstore ON #testIdentityCompression_sortedColumnstore (i) WITH (MAXDOP = 1)
GO
-- Create another table with the same data, but randomly ordered
SELECT *
INTO #testIdentityCompression_randomOrderColumnstore
FROM #testIdentityCompression_sortedColumnstore
GO
ALTER TABLE #testIdentityCompression_randomOrderColumnstore
ADD UNIQUE CLUSTERED (randCol)
GO
CREATE NONCLUSTERED COLUMNSTORE INDEX cs_#testIdentityCompression_randomOrderColumnstore ON #testIdentityCompression_randomOrderColumnstore (i) WITH (MAXDOP = 1)
GO
-- Create a b-tree with the identity column data and no compression
-- Note that we copy over only the identity column since we'll be looking at the total size of the b-tree index
-- If anything, this gives an unfair "advantage" to the rowstore-page-compressed version since more
-- rows fit on a page and page compression rates should be better without the "randCol" column.
SELECT i
INTO #testIdentityCompression_uncompressedRowstore
FROM #testIdentityCompression_sortedColumnstore
GO
ALTER TABLE #testIdentityCompression_uncompressedRowstore
ADD PRIMARY KEY (i)
GO
-- Create a b-tree with the identity column and page compression
SELECT i
INTO #testIdentityCompression_compressedRowstore
FROM #testIdentityCompression_sortedColumnstore
GO
ALTER TABLE #testIdentityCompression_compressedRowstore
ADD PRIMARY KEY (i)
WITH (DATA_COMPRESSION = PAGE)
GO
-- Compare all the sizes!
SELECT OBJECT_NAME(p.object_id, 2) AS tableName, COUNT(*) AS num_segments, SUM(on_disk_size / (1024.*1024.)) as size_mb
FROM tempdb.sys.partitions p
JOIN tempdb.sys.column_store_segments s
ON s.partition_id = p.partition_id
AND s.column_id = 1
WHERE p.object_id IN (OBJECT_ID('tempdb..#testIdentityCompression_sortedColumnstore'),OBJECT_ID('tempdb..#testIdentityCompression_randomOrderColumnstore'))
GROUP BY p.object_id
UNION ALL
SELECT OBJECT_NAME(p.object_id, 2) AS tableName
, NULL AS num_segments
, (a.total_pages*8.0) / (1024.0) as size_mb
FROM tempdb.sys.partitions p
JOIN tempdb.sys.allocation_units a
ON a.container_id = p.partition_id
WHERE p.object_id IN (OBJECT_ID('tempdb..#testIdentityCompression_compressedRowstore'),OBJECT_ID('tempdb..#testIdentityCompression_uncompressedRowstore'))
ORDER BY 3 ASC
GO