¿Qué factores entran en el índice agrupado de una vista indexada que se selecciona?

19

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

11 filas de salida: lo mismo para ambas consultas

Salida del generador de perfiles
Las 4 líneas superiores no tienen ninguna pista. Las 4 líneas inferiores están usando la pista.

Profiler

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.
Sin pistas: gran plan de ejecución

Plan simple cuando se utiliza una pista.

Uso de pista: plan de ejecución simple

EBarr
fuente
La discusión se movió al chat
Paul White dice GoFundMonica

Respuestas:

26

La coincidencia de vistas indexadas es una operación relativamente costosa *, por lo que el optimizador intenta primero otras transformaciones rápidas y fáciles. Si se produce un plan económico (0,05 unidades en su caso), la optimización finaliza antes de tiempo. La apuesta es que la optimización continua consumiría más tiempo del que ahorró. Recuerde que el objetivo principal del optimizador es un plan "suficientemente bueno" rápidamente.

Usar el índice agrupado en la vista no es costoso en sí mismo, pero puede ser el proceso de hacer coincidir un árbol de consulta lógica con vistas indexadas potenciales. Como mencioné en un comentario sobre la otra pregunta, la referencia de la vista en la consulta se expande antes de la optimización, por lo que el optimizador no sabe que escribió la consulta en la vista en primer lugar: solo ve el árbol expandido (como si la vista había sido alineada).

"Buen plan suficiente" significa que el optimizador encontró un plan decente y se detuvo temprano en una fase de exploración. "TimeOut" significa que excedió el número de pasos de optimización que se estableció como un "presupuesto" al comienzo de la fase actual.

El presupuesto se establece en función del costo del mejor plan encontrado en una fase anterior. Con una consulta de tan bajo costo (0.05), el número de movimientos presupuestados será bastante pequeño y se agotará rápidamente por la transformación regular dada la cantidad de uniones involucradas en su consulta de muestra (hay muchas maneras de reorganizar las uniones internas, por ejemplo) .

Si está interesado en saber más acerca de por qué la coincidencia de vistas indexadas es costosa y, por lo tanto, se dejó para las etapas posteriores de optimización y / o solo se consideró para consultas más costosas, hay dos documentos de investigación de Microsoft sobre el tema aquí (pdf) y aquí (citeseer )

Otro factor relevante es que la coincidencia de vista indexada no está disponible en la fase de optimización 0 (procesamiento de transacciones).

Otras lecturas:

Vistas indexadas y estadísticas

* y solo disponible en Enterprise Edition (o equivalente)

Paul White dice GoFundMonica
fuente