Los porcentajes entre los planes de consulta no tienen sentido para comparar directamente. Debe comparar las consultas para tener una comparación válida. Además, los recuentos de filas pequeñas tienden a ocultar las diferencias de rendimiento entre las estrategias de indexación. Al aumentar el recuento de filas a 10 millones, puede obtener una imagen más clara de las diferencias de rendimiento.
Hay un script de muestra que crea 3 tablas, las dos de arriba y una tercera con un índice agrupado y no agrupado.
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)
Rellene las tablas con 10 millones de filas.
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'
Podemos usar sys.dm_db_index_physical_stats para ver el tamaño en el disco de los índices.
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
Y los resultados:
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
El índice agrupado de T1 es de alrededor de 1,6 GB de tamaño. El índice no agrupado de T2 es de 170 MB (90% de ahorro en IO). El índice no agrupado de T3 es de 97 MB, o aproximadamente un 95% menos de IO que T1.
Entonces, basado en el IO requerido, el plan de consulta original debería haber estado más en la línea del 10% / 90%, no del 38% / 62%. Además, dado que es probable que el índice no agrupado se ajuste completamente en la memoria, la diferencia puede ser aún mayor, ya que el disco IO es muy costoso.