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.
FOJNtoLSJNandLASJN
) que da como resultado que las cosas no funcionen como esperamos, y que deja basura (BaseRow / Checksums) que es útil en algunos tipos de planes (por ejemplo, cursores) pero que no es necesario aquí.Chk
es suma de comprobación! Gracias no estaba seguro de eso. Originalmente estaba pensando que podría tener algo que ver con las restricciones de verificación.Aunque esto podría ser una coincidencia debido a la naturaleza artificial de sus datos de prueba, como mencionó SQL 2012 probé una reescritura:
Esto produjo un buen plan de bajo costo usando su índice y con lecturas significativamente más bajas que las otras opciones (y los mismos resultados para sus datos de prueba).
Sospecho que sus datos reales son más complicados, por lo que puede haber algunos escenarios en los que esta consulta se comporta semánticamente diferente a la suya, pero a veces muestra que las nuevas características pueden marcar una verdadera diferencia.
Experimenté con algunos datos más variados y encontré algunos escenarios que coincidían y otros no:
fuente
comp
no es una columna calculada, no verá el orden.LEAD
función funcionó exactamente como me gustaría en mi instancia local de 2012 express. Desafortunadamente, este inconveniente menor para mí no se consideró una razón suficiente para actualizar los servidores de producción todavía ...Cuando intenté realizar las mismas acciones, obtuve los otros resultados. En primer lugar, mi plan de ejecución para la tabla sin índices tiene el siguiente aspecto:
Como podemos ver en el Análisis de índice agrupado (t2), el predicado se usa para determinar las filas necesarias que se devolverán (debido a la condición):
Cuando se agregó el índice, no importa si fue definido por el operador WITH o no, el plan de ejecución se convirtió en el siguiente:
Como podemos ver, la exploración de índice agrupado se reemplaza por la exploración de índice. Como vimos anteriormente, el SQL Server usa las columnas de origen de la columna calculada para realizar la coincidencia de la consulta anidada. Durante el análisis de índice agrupado, todos estos valores se pueden adquirir al mismo tiempo (no se necesitan operaciones adicionales). Cuando se agregó el índice, el filtrado de las filas necesarias de la tabla (en la selección principal) se realiza de acuerdo con el índice, pero los valores de las columnas de origen para la columna calculada
comp
aún deben obtenerse (última operación Nested Loop) .Debido a esto, se utiliza la operación de búsqueda de clave para obtener los datos de las columnas de origen de la calculada.
PD Parece un error en SQL Server.
fuente