Quais fatores entram no Índice Clusterizado de uma Visualização Indexada sendo selecionado?

19

Resumidamente,
quais são os fatores que consultam a seleção do otimizador do índice de uma exibição indexada?

Para mim, as visualizações indexadas parecem desafiar o que eu entendo sobre como o Optimizer seleciona os índices. Já vi isso perguntado antes , mas o OP não foi muito bem recebido. Estou realmente procurando guias , mas vou inventar um pseudo exemplo, depois postarei um exemplo real com bastante DDL, saída, exemplos.

Suponha que eu esteja usando o Enterprise 2008+, entenda with(noexpand)

Exemplo pseudo

Tomemos este pseudo exemplo: eu crio uma visualização com 22 junções, 17 filtros e um pônei de circo que atravessa um monte de 10 milhões de tabelas de linhas. Essa visão é cara (sim, com E maiúsculo) para se materializar. Vou SCHEMABIND e indexar a exibição. Então a SELECT a,b FROM AnIndexedView WHERE theClusterKeyField < 84. Na lógica do Optimizer que me ilude, as junções subjacentes são executadas.

O resultado:

  • Nenhuma dica: 4825 lê para 720 linhas, 47 cpu por 76ms e um custo estimado da subárvore de 0,30523.
  • Com dica: 17 leituras, 720 linhas, 15 cpu por 4ms e um custo estimado da subárvore de 0,007253

Então, o que está acontecendo aqui? Eu tentei no Enterprise 2008, 2008-R2 e 2012. A cada métrica que consigo pensar em usar o índice do modo de exibição, é muito mais eficiente. Não tenho problema de detecção de parâmetros ou dados distorcidos, pois isso é ad hock.

Um exemplo real (longo)

A menos que você seja masoquista, provavelmente não precisa ou deseja ler esta parte.

A versão
Sim, empresa.

Microsoft SQL Server 2012 - 11.0.2100.60 (X64) 10 de fevereiro de 2012 19:39:15 Direitos autorais (c) Microsoft Corporation Enterprise Edition (64 bits) no Windows NT 6.2 (Build 9200:) (Hypervisor)

A 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)

Teste 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 linhas de saída

11 linhas de saída - o mesmo para as duas consultas

Saída do Profiler
As 4 principais linhas estão sem uma dica. As 4 linhas inferiores estão usando a dica.

analisador

Execução Planos
GitHub Gist para ambos os planos de execução em formato SQLPlan

Nenhum plano de execução de dica - por que não usar o índice clusterizado que eu lhe dei, Sr. SQL? Está agrupado nos 3 campos de filtro. Experimente, você pode gostar.
No Hint - grande plano de execução

Plano simples ao usar uma dica.

Usando dica - Plano de execução simples

EBarr
fonte
Discussão mudou para o bate-papo
Paul White diz GoFundMonica

Respostas:

26

A correspondência de visualizações indexadas é uma operação relativamente cara *, portanto, o otimizador tenta outras transformações rápidas e fáceis primeiro. Se eles produzirem um plano barato (0,05 unidades no seu caso), a otimização terminará mais cedo. A aposta é que a otimização contínua consumiria mais tempo do que economizou. Lembre-se de que o objetivo principal do otimizador é um plano "suficientemente bom" rapidamente.

O uso do índice clusterizado na exibição não é caro por si só, mas o processo de correspondência de uma árvore de consultas lógicas com as possíveis exibições indexadas pode ser. Como mencionei em um comentário sobre a outra pergunta, a referência de exibição na consulta é expandida antes da otimização, portanto, o otimizador não sabe que você escreveu a consulta na exibição em primeiro lugar - ele vê apenas a árvore expandida (como se a vista estava alinhada).

"Bom plano suficiente" significa que o otimizador encontrou um plano decente e parou no início de uma fase de exploração. "TimeOut" significa que excedeu o número de etapas de otimização que definiu como um 'orçamento' no início da fase atual.

O orçamento é definido com base no custo do melhor plano encontrado em uma fase anterior. Com uma consulta de baixo custo (0,05), o número de movimentações orçadas será bastante pequeno e rapidamente esgotado pela transformação regular, dado o número de junções envolvidas na consulta de amostra (existem várias maneiras de reorganizar as junções internas, por exemplo) .

Se você estiver interessado em saber mais sobre por que a correspondência de exibição indexada é cara e, portanto, deixada para estágios posteriores de otimização e / ou considerada apenas para consultas mais caras, existem dois documentos de pesquisa da Microsoft sobre o tópico aqui (pdf) e aqui (citeseer )

Outro fator relevante é que a correspondência de exibição indexada não está disponível na fase de otimização 0 (processamento de transações).

Leitura adicional:

Visualizações indexadas e estatísticas

* e disponível apenas na Enterprise Edition (ou equivalente)

Paul White diz que a GoFundMonica
fonte