Brevemente,
¿qué factores intervienen en la selección del índice de una vista indexada por parte del optimizador?
Para mí, las vistas indexadas parecen desafiar lo que entiendo acerca de cómo el Optimizador selecciona los índices. He visto esto antes , pero el OP no fue muy bien recibido. Realmente estoy buscando guías , pero inventaré un pseudo ejemplo, luego publicaré un ejemplo real con mucho DDL, salida, ejemplos.
Supongamos que estoy usando Enterprise 2008+, entiendo
with(noexpand)
Pseudo Ejemplo
Tome este pseudo ejemplo: creo una vista con 22 combinaciones, 17 filtros y un pony de circo que cruza un grupo de 10 millones de tablas de filas. Esta vista es costosa (sí, con una E mayúscula) para materializarse. ESQUEMARÉ e indexaré la vista. Entonces un SELECT a,b FROM AnIndexedView WHERE theClusterKeyField < 84
. En la lógica del Optimizador que me elude, se realizan las uniones subyacentes.
El resultado:
- Sin sugerencia: 4825 lecturas para 720 filas, 47 cpu en 76 ms y un costo estimado de subárbol de 0.30523.
- Con Sugerencia: 17 lecturas, 720 filas, 15 cpu en 4 ms y un costo estimado de subárbol de 0.007253
Entonces, ¿qué está pasando aquí? Lo probé en Enterprise 2008, 2008-R2 y 2012. Por cada métrica que se me ocurre, usar el índice de la vista es mucho más eficiente. No tengo un problema de detección de parámetros o datos asimétricos, ya que esto es ad hock.
Un ejemplo real (largo)
A menos que seas un poco masoquista, probablemente no necesites o quieras leer esta parte.
La versión
Sí, empresa.
Microsoft SQL Server 2012 - 11.0.2100.60 (X64) 10 de febrero de 2012 19:39:15 Copyright (c) Microsoft Corporation Enterprise Edition (64 bits) en Windows NT 6.2 (compilación 9200:) (hipervisor)
La vista
CREATE VIEW dbo.TimelineMaterialized WITH SCHEMABINDING
AS
SELECT TM.TimelineID,
TM.TimelineTypeID,
TM.EmployeeID,
TM.CreateUTC,
CUL.CultureCode,
CASE
WHEN TM.CustomerMessageID > 0 THEN TM.CustomerMessageID
WHEN TM.CustomerSessionID > 0 THEN TM.CustomerSessionID
WHEN TM.NewItemTagID > 0 THEN TM.NewItemTagID
WHEN TM.OutfitID > 0 THEN TM.OutfitID
WHEN TM.ProductTransactionID > 0 THEN TM.ProductTransactionID
ELSE 0 END As HrefId,
CASE
WHEN TM.CustomerMessageID > 0 THEN IsNull(C.Name, 'N/A')
WHEN TM.CustomerSessionID > 0 THEN IsNull(C.Name, 'N/A')
WHEN TM.NewItemTagID > 0 THEN IsNull(NI.Title, 'N/A')
WHEN TM.OutfitID > 0 THEN IsNull(O.Name, 'N/A')
WHEN TM.ProductTransactionID > 0 THEN IsNull(PT_PL.NameLocalized, 'N/A')
END as HrefText
FROM dbo.Timeline TM
INNER JOIN dbo.CustomerSession CS ON TM.CustomerSessionID = CS.CustomerSessionID
INNER JOIN dbo.CustomerMessage CM ON TM.CustomerMessageID = CM.CustomerMessageID
INNER JOIN dbo.Outfit O ON PO.OutfitID = O.OutfitID
INNER JOIN dbo.ProductTransaction PT ON TM.ProductTransactionID = PT.ProductTransactionID
INNER JOIN dbo.Product PT_P ON PT.ProductID = PT_P.ProductID
INNER JOIN dbo.ProductLang PT_PL ON PT_P.ProductID = PT_PL.ProductID
INNER JOIN dbo.Culture CUL ON PT_PL.CultureID = CUL.CultureID
INNER JOIN dbo.NewsItemTag NIT ON TM.NewsItemTagID = NIT.NewsItemTagID
INNER JOIN dbo.NewsItem NI ON NIT.NewsItemID = NI.NewsItemID
INNER JOIN dbo.Customer C ON C.CustomerID = CASE
WHEN TM.TimelineTypeID = 1 THEN CM.CustomerID
WHEN TM.TimelineTypeID = 5 THEN CS.CustomerID
ELSE 0 END
WHERE CUL.IsActive = 1
Índice agrupado
CREATE UNIQUE CLUSTERED INDEX PK_TimelineMaterialized ON
TimelineMaterialized (EmployeeID, CreateUTC, CultureCode, TimelineID)
Prueba SQL
-- NO HINT - - - - - - - - - - - - - - -
SELECT * --yes yes, star is bad ...just a test example
FROM TimelineMaterialized TM
WHERE
TM.EmployeeID = 2
AND TM.CultureCode = 'en-US'
AND TM.CreateUTC > '9/10/2012'
AND TM.CreateUTC < '9/11/2012'
-- WITH HINT - - - - - - - - - - - - - - -
SELECT *
FROM TimelineMaterialized TM with(noexpand)
WHERE
TM.EmployeeID = 2
AND TM.CultureCode = 'en-US'
AND TM.CreateUTC > '9/10/2012'
AND TM.CreateUTC < '9/11/2012'
Resultado = 11 filas de salida
Salida del generador de perfiles
Las 4 líneas superiores no tienen ninguna pista. Las 4 líneas inferiores están usando la pista.
Planes de ejecución
GitHub Gist para ambos Planes de ejecución en formato SQLPlan
Sin plan de ejecución de pistas: ¿por qué no usar el índice agrupado que le di al Sr. SQL? Está agrupado en los 3 campos de filtro. Inténtalo, puede que te guste.
Plan simple cuando se utiliza una pista.