Plano de Execução como Consulta
SELECT VP.Branch
, VP.ROUTE AS Route
, VP.SAPCustomerID
, S.CustomerID
, S.ProductID
, S.Date
-- Group by Customer
, CustomerQuantity = SUM(S.Quantity) OVER (PARTITION BY VP.Branch, VP.ROUTE, VP.SAPCustomerID, S.ProductID, S.Date)
, CustomerFourWeekSalesAvg = SUM(S.FourWeekSalesAvg) OVER (PARTITION BY VP.Branch, VP.ROUTE, VP.SAPCustomerID, S.ProductID, S.Date)
-- Group by Route
, SUM(S.Quantity) OVER (PARTITION BY VP.Branch, VP.ROUTE, S.ProductID, S.Date) AS RouteQuantity
, RouteFourWeekSalesAvg = SUM(S.FourWeekSalesAvg) OVER (PARTITION BY VP.Branch, VP.ROUTE, S.ProductID, S.Date)
-- Group by Branch
, BranchQuantity = SUM(S.Quantity) OVER (PARTITION BY VP.Branch, S.ProductID, S.Date)
, BranchFourWeekSalesAvg = SUM(S.FourWeekSalesAvg) OVER (PARTITION BY VP.Branch, S.ProductID, S.Date)
FROM vw_SalesByWeek AS S
INNER JOIN SAP_VisitPlan AS VP WITH (NOLOCK)
ON VP.CustomerID = S.CustomerID
AND VP.DateFrom <= S.Date
AND VP.DateTo >= S.Date
Plano de Execução como Visualização
-- Where vw_SalesByWeekSummary is the query above exactly.
SELECT [Branch]
,[Route]
,[SAPCustomerID]
,[CustomerID]
,[ProductID]
,[Date]
,[CustomerQuantity]
,[CustomerFourWeekSalesAvg]
,[RouteQuantity]
,[RouteFourWeekSalesAvg]
,[BranchQuantity]
,[BranchFourWeekSalesAvg]
FROM vw_SalesByWeekSummary
WHERE Route = '0600'
Problema
A consulta sozinha ou como um procedimento armazenado funciona bem; no entanto, a consulta como uma exibição decide fazer varreduras em vez de buscas e usa índices diferentes. Como posso fazer com que a consulta se comporte corretamente como uma exibição? O que está causando o uso de diferentes índices e varredura em vez de busca?
Arquivos SQLPlan
Índices
-- Solo Query Plan Indexes
CREATE NONCLUSTERED INDEX [IX_VisitPlan_ByRoute] ON [dbo].[SAP_VisitPlan] ([ROUTE] ASC) INCLUDE ([Branch])
CREATE UNIQUE NONCLUSTERED INDEX [UIX_CacheCS_ByWeek] ON [dbo].[Cache_ConvSalesByWeek] ([CustomerID] ASC, [ProductID] ASC, [WkStartDate] ASC) INCLUDE ([ID], [SoldQuantity], [Route], [FourWeekSalesAvg], [NumberOfPriorSalesWeeks])
CREATE NONCLUSTERED INDEX [IX_CacheSS_4wkAvg] ON [dbo].[Cache_ScanSalesByWeek] ([CustomerID] ASC, [ProductID] ASC, [Route] ASC) INCLUDE ([FourWeekSalesAvg], [WkStartDate], [SoldQuantity])
-- View Query Plan Indexes
CREATE UNIQUE NONCLUSTERED INDEX [UIX_VisitPlan_PK] ON [dbo].[SAP_VisitPlan] ([SAPCustomerID] ASC, [CustomerID] ASC, [DateTo] DESC, [DateFrom] DESC, [ROUTE] ASC, [DriverNumber] ASC) INCLUDE ([Branch])
CREATE NONCLUSTERED INDEX [IX_CacheCS] ON [dbo].[Cache_ConvSalesByWeek] ([CustomerID] ASC, [ProductID] ASC, [WkStartDate] ASC) INCLUDE ([SoldQuantity], [FourWeekSalesAvg], [Route])
CREATE NONCLUSTERED INDEX [IX_ScanSalesByWeek_Sunday] ON [dbo].[Cache_ScanSalesByWeek] ([WkStartDate] ASC) INCLUDE ([CustomerID], [ProductID], [SoldQuantity], [Route], [FourWeekSalesAvg])
O problema essencial é que filtrar
Route = N'0600'
antes de calcular as funções da janela (como na consulta) é diferente de filtrarRoute = N'0600'
depois de calcular as funções da janela (como na exibição).Isso daria resultados diferentes (= incorretos) para as funções da janela em geral, então o otimizador não faz isso. Consulte Of Window Functions And Where Clauses , de Erik Darling, para obter mais informações.
Se todas as funções de janela na exibição fossem particionadas em
Route
, o otimizador consideraria empurrar o predicado para baixo na exibição porque os resultados corretos ainda seriam obtidos. Infelizmente, esse não é o caso da sua opinião. AdicionarOPTION (RECOMPILE)
não ajudaria neste caso.Considere reescrever (ou complementar) a exibição como uma função com valor de tabela embutida, com um parâmetro explícito para
Route
. Eu tenho um exemplo da técnica em uma resposta no Stack Overflow.Se você tiver outros critérios de filtro além
Route
de sozinho e se tiver um procedimento armazenado de qualquer maneira, poderá abstrair o tipo de critério de filtro - faça um TVF para rota e, quando a rota for passada para o procedimento armazenado, chame-o . Faça outro TVF para o intervalo de datas e, quando as datas forem passadas, chame-o. Fica complicado quando eles passam rota e datas, se isso for possível, mas a separação provavelmente será a maneira mais segura de obter um desempenho previsível de maneira confiável, independentemente de qual parâmetro é passado. Se você tiver critérios de filtro opcionais, pode ser que SQL dinâmico e/ou opção (recompilar) seja sua melhor aposta.