查询速度很快,但在创建为视图时变得缓慢

She*_*115 6 performance sql-server execution-plan view sql-server-2016 query-performance

执行计划作为查询

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
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

执行计划作为视图

-- 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'
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

问题

查询单独或作为存储过程工作得很好;但是,作为视图的查询决定进行扫描而不是查找并使用不同的索引。如何让查询作为视图正常运行?是什么导致它使用不同的索引和扫描而不是搜索?

SQL计划文件

索引

-- 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])
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 11

基本问题是Route = N'0600' 计算窗口函数之前过滤(如在查询中)与Route = N'0600' 计算窗口函数之后过滤(如在视图中)不同。

这通常会为窗口函数提供不同(= 不正确)的结果,因此优化器不会这样做。有关更多信息,请参阅Erik Darling 的Of Windowing Functions And Where Clauses

如果视图中的所有窗口函数都在 上进行分区Route,优化器会考虑将谓词下推到视图中,因为仍然会获得正确的结果。遗憾的是,您的观点并非如此。OPTION (RECOMPILE)在这种情况下,添加无济于事。

考虑将视图重写(或补充)为内联表值函数,并为Route. 我在 Stack Overflow 上的回答中有一个该技术的例子。

如果您有其他过滤条件Route,并且无论如何您都将拥有一个存储过程,您可以抽象出过滤条件的类型 - 为路由创建一个 TVF,当路由传递给存储过程时,调用它. 为日期范围制作另一个 TVF,当日期过去时,调用它。当它们同时传递路由和日期时会变得复杂(如果可能的话),但无论传递哪个参数,分离都可能是获得可靠可预测性能的最安全方法。如果您有可选的过滤条件,那么动态 SQL 和/或选项(重新编译)可能是您最好的选择。