Tengo una columna calculada persistente en una tabla que simplemente está formada por columnas concatenadas, por ejemplo
CREATE TABLE dbo.T
(
ID INT IDENTITY(1, 1) NOT NULL CONSTRAINT PK_T_ID PRIMARY KEY,
A VARCHAR(20) NOT NULL,
B VARCHAR(20) NOT NULL,
C VARCHAR(20) NOT NULL,
D DATE NULL,
E VARCHAR(20) NULL,
Comp AS A + '-' + B + '-' + C PERSISTED NOT NULL
);
En esto Comp
no es único, y D es la fecha de inicio de validez de cada combinación de A, B, C
, por lo tanto, uso la siguiente consulta para obtener la fecha de finalización de cada uno A, B, C
(básicamente la próxima fecha de inicio para el mismo valor de Comp):
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1
WHERE t1.D IS NOT NULL -- DON'T CARE ABOUT INACTIVE RECORDS
ORDER BY t1.Comp;
Luego agregué un índice a la columna calculada para ayudar en esta consulta (y también en otras):
CREATE NONCLUSTERED INDEX IX_T_Comp_D ON dbo.T (Comp, D) WHERE D IS NOT NULL;
Sin embargo, el plan de consulta me sorprendió. Pensé que, dado que tengo una cláusula where que indica eso D IS NOT NULL
y estoy clasificando por Comp
, y no haciendo referencia a ninguna columna fuera del índice, el índice en la columna calculada podría usarse para escanear t1 y t2, pero vi un índice agrupado escanear.
Así que forcé el uso de este índice para ver si producía un plan mejor:
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1 WITH (INDEX (IX_T_Comp_D))
WHERE t1.D IS NOT NULL
ORDER BY t1.Comp;
Que dio este plan
Esto muestra que se está utilizando una búsqueda de claves, cuyos detalles son:
Ahora, de acuerdo con la documentación del servidor SQL:
Puede crear un índice en una columna calculada que se define con una expresión determinista, pero imprecisa, si la columna está marcada como PERSISTA en la instrucción CREATE TABLE o ALTER TABLE. Esto significa que el Motor de base de datos almacena los valores calculados en la tabla y los actualiza cuando se actualizan cualesquiera otras columnas de las que depende la columna calculada. El Motor de base de datos utiliza estos valores persistentes cuando crea un índice en la columna y cuando se hace referencia al índice en una consulta. Esta opción le permite crear un índice en una columna calculada cuando Database Engine no puede probar con precisión si una función que devuelve expresiones de columna calculadas, particularmente una función CLR que se crea en .NET Framework, es determinista y precisa.
Entonces, si, como dicen los documentos, "el Motor de base de datos almacena los valores calculados en la tabla" , y el valor también se almacena en mi índice, ¿por qué se requiere una Búsqueda de clave para obtener A, B y C cuando no se hace referencia en ellos? la consulta en absoluto? Supongo que se están utilizando para calcular Comp, pero ¿por qué? Además, ¿por qué la consulta puede usar el índice activado t2
, pero no activado t1
?
Nota: he etiquetado SQL Server 2008 porque esta es la versión en la que se encuentra mi problema principal, pero también tengo el mismo comportamiento en 2012.