Le percentuali tra i piani di query non hanno senso per il confronto definitivo. È necessario confrontare le query per avere un confronto valido. Inoltre, i conteggi di piccole righe hanno la tendenza a nascondere le differenze di prestazioni tra le strategie di indicizzazione. Aumentando il conteggio delle righe a 10 milioni è possibile ottenere un quadro più chiaro delle differenze di prestazioni.
Esiste uno script di esempio che crea 3 tabelle, le tue due dall'alto e una terza con un indice sia cluster che non cluster.
USE [tempdb]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[t1](
[id] [int] IDENTITY(1,1) NOT NULL,
[c1] [varchar](200) NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[t2](
[id] [int] IDENTITY(1,1) NOT NULL,
[c1] [varchar](200) NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[t3](
[id] [int] IDENTITY(1,1) NOT NULL,
[c1] [varchar](200) NULL
) ON [PRIMARY]
GO
CREATE CLUSTERED INDEX CIX_t1 ON t1(id)
CREATE NONCLUSTERED INDEX IX_t2 ON t2(id)
CREATE CLUSTERED INDEX CIX_t3 ON t3(id)
CREATE NONCLUSTERED INDEX IX_t3 ON t3(id)
Popolare le tabelle con 10 milioni di righe
DECLARE @i INT
DECLARE @j int
DECLARE @t DATETIME
SET NOCOUNT ON
SET @t = CURRENT_TIMESTAMP
SET @i = 0
WHILE @i < 10000000
BEGIN
--populate with strings with a length between 100 and 200
INSERT INTO t1 (c1) VALUES (REPLICATE('x', 101+ CAST(RAND(@i) * 100 AS INT)))
SET @i = @i + 1
END
PRINT 'Time to populate t1: '+ CAST(DATEDIFF(ms, @t, CURRENT_TIMESTAMP) AS VARCHAR(10)) + ' ms'
SET @t = CURRENT_TIMESTAMP
SET @i = 0
WHILE @i < 10000000
BEGIN
--populate with strings with a length between 100 and 200
INSERT INTO t2 (c1) VALUES (REPLICATE('x', 101+ CAST(RAND(@i) * 100 AS INT)))
SET @i = @i + 1
END
PRINT 'Time to populate t3: '+ CAST(DATEDIFF(ms, @t, CURRENT_TIMESTAMP) AS VARCHAR(10)) + ' ms'
SET @t = CURRENT_TIMESTAMP
SET @i = 0
WHILE @i < 10000000
BEGIN
--populate with strings with a length between 100 and 200
INSERT INTO t3 (c1) VALUES (REPLICATE('x', 101+ CAST(RAND(@i) * 100 AS INT)))
SET @i = @i + 1
END
PRINT 'Time to populate t3: '+ CAST(DATEDIFF(ms, @t, CURRENT_TIMESTAMP) AS VARCHAR(10)) + ' ms'
Possiamo usare sys.dm_db_index_physical_stats per vedere la dimensione sul disco degli indici.
SELECT OBJECT_NAME(OBJECT_ID) table_name, index_id, index_type_desc,
record_count, page_count, page_count / 128.0 size_in_mb, avg_record_size_in_bytes
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('t1'), NULL, NULL, 'detailed')
WHERE index_level = 0
UNION ALL
SELECT OBJECT_NAME(OBJECT_ID) table_name, index_id, index_type_desc,
record_count, page_count, page_count / 128.0 size_in_mb, avg_record_size_in_bytes
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('t2'), NULL, NULL, 'detailed')
WHERE index_level = 0
UNION ALL
SELECT OBJECT_NAME(OBJECT_ID) table_name, index_id, index_type_desc,
record_count, page_count, page_count / 128.0 size_in_mb, avg_record_size_in_bytes
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('t3'), NULL, NULL, 'detailed')
WHERE index_level = 0
E i risultati:
table_name index_id page_count size_in_mb avg_record_size_in_bytes index_type_desc
t1 1 211698 1653.890625 167.543 CLUSTERED INDEX
t2 0 209163 1634.085937 165.543 HEAP
t2 2 22272 174.000000 16 NONCLUSTERED INDEX
t3 1 211698 1653.890625 167.543 CLUSTERED INDEX
t3 2 12361 96.570312 8 NONCLUSTERED INDEX
L'indice cluster di T1 ha una dimensione di circa 1,6 GB. L'indice non cluster di T2 è di 170 MB (risparmio del 90% in I / O). L'indice non cluster di T3 è 97 MB, ovvero circa il 95% in meno di IO rispetto a T1.
Pertanto, in base all'IO richiesto, il piano di query originale avrebbe dovuto essere più lungo il 10% / 90%, non il 38% / 62%. Inoltre, poiché è probabile che l'indice non cluster si adatti interamente alla memoria, la differenza potrebbe essere ancora maggiore, poiché l'IO del disco è molto costoso.