删除 where 子句然后添加回来时的执行计划优化

Posted

技术标签:

【中文标题】删除 where 子句然后添加回来时的执行计划优化【英文标题】:Execution Plan Optimization when where clause is removed then added back 【发布时间】:2012-10-15 21:34:00 【问题描述】:

我有一个使用表值函数的存储过程,该函数在 9 秒内执行。如果我更改表值函数并删除 where 子句,则存储过程将在 3 秒内执行。如果我重新添加 where 子句,查询仍会在 3 秒内执行。

我查看了执行计划,似乎在删除 where 子句后,执行计划包括并行性,并且我的 2 个表的扫描计数从 50000 和 65000 下降到 5 和 3。添加后where 子句返回,优化的执行计划仍然运行,除非我运行 DBCC FREEPROCCACHE。

问题 1. 为什么只有在我第一次删除 where 子句时,SQL Server 才开始对两个查询使用优化的执行计划?

    有没有办法强制 SQL Server 使用这个执行计划?

此外,这是一个参数化的一体式查询,它在 where 子句中使用(参数为 null 或参数),我认为这对性能不利。

RETURNS TABLE 
AS
RETURN 
(
SELECT  TOP (@PageNumber * @PageSize)
                CASE
                    WHEN @SortOrder = 'Expensive' THEN ROW_NUMBER()     OVER (ORDER BY SellingPrice DESC)
                WHEN @SortOrder = 'Inexpensive' THEN ROW_NUMBER() OVER (ORDER BY SellingPrice ASC)                  
                WHEN @SortOrder = 'LowMiles' THEN ROW_NUMBER() OVER (ORDER BY Mileage ASC)
                WHEN @SortOrder = 'HighMiles' THEN ROW_NUMBER() OVER (ORDER BY Mileage DESC)
                WHEN @SortOrder = 'Closest' THEN ROW_NUMBER() OVER (ORDER BY P1.Distance ASC)       
                WHEN @SortOrder = 'Newest' THEN ROW_NUMBER() OVER (ORDER BY [Year] DESC)    
                WHEN @SortOrder = 'Oldest' THEN ROW_NUMBER() OVER (ORDER BY [Year] ASC)                     
                ELSE ROW_NUMBER() OVER (ORDER BY InventoryID ASC)
            END as rn,
            P1.InventoryID,
            P1.SellingPrice,
            P1.Distance,
            P1.Mileage,
            Count(*) OVER () RESULT_COUNT,
            dimCarStatus.[year]
    FROM    (SELECT InventoryID, SellingPrice, Zip.Distance, Mileage, ColorKey, CarStatusKey, CarKey FROM facInventory
                JOIN @ZipCodes Zip
                ON   Zip.DealerKey = facInventory.DealerKey) as P1
    JOIN    dimColor
            ON dimColor.ColorKey = P1.ColorKey
    JOIN    dimCarStatus
            ON dimCarStatus.CarStatusKey = P1.CarStatusKey  
    JOIN    dimCar
            ON dimCar.CarKey = P1.CarKey                        
    WHERE
            (@ExteriorColor is NULL OR dimColor.ExteriorColor like @ExteriorColor) AND
            (@InteriorColor is NULL OR dimColor.InteriorColor like @InteriorColor) AND
            (@Condition is NULL OR dimCarStatus.Condition like @Condition) AND
            (@Year is NULL OR dimCarStatus.[Year] like @Year) AND
            (@Certified is NULL OR dimCarStatus.Certified like @Certified) AND
            (@Make is NULL OR dimCar.Make like @Make) AND
            (@ModelCategory is NULL OR dimCar.ModelCategory like @ModelCategory) AND    
            (@Model is NULL OR dimCar.Model like @Model) AND
            (@Trim is NULL OR dimCar.Trim like @Trim) AND
            (@BodyType is NULL OR dimCar.BodyType like @BodyType) AND
            (@VehicleTypeCode is NULL OR dimCar.VehicleTypeCode like @VehicleTypeCode) AND
            (@MinPrice is NULL OR P1.SellingPrice >= @MinPrice) AND
            (@MaxPrice is NULL OR P1.SellingPrice < @MaxPrice) AND
            (@Mileage is NULL OR P1.Mileage < @Mileage)
    ORDER   BY
            CASE
                WHEN @SortOrder = 'Expensive' THEN -SellingPrice
                WHEN @SortOrder = 'Inexpensive' THEN SellingPrice 
                WHEN @SortOrder = 'LowMiles' THEN Mileage
                WHEN @SortOrder = 'HighMiles' THEN -Mileage
                WHEN @SortOrder = 'Closest' THEN P1.Distance        
                WHEN @SortOrder = 'Newest' THEN -[YEAR]
                WHEN @SortOrder = 'Oldest' THEN [YEAR]                  
                ELSE InventoryID 
            END
)

【问题讨论】:

您可以通过在参数 where 子句中使用 ISNULL 而不是 OR 来获得更好的性能。 WHERE dimColor.ExteriorColor like ISNULL(@ExteriorColor,'%') 【参考方案1】:

问题 1:SQL server 仍然继续使用对没有 where 子句的语句有效的执行计划。基本上 SQL Server 认为 where 子句不存在。

问题2:使用OPTION(USE PLAN N'')文章:http://technet.microsoft.com/en-us/library/cc917694.aspx

个人推荐:

    更改查询,使您至少拥有一个强制过滤器并将其编入索引。 或 将其更改为动态,并动态应用过滤器。文章:http://msdn.microsoft.com/en-us/library/ms188001.aspx

希望这会有所帮助。

【讨论】:

以上是关于删除 where 子句然后添加回来时的执行计划优化的主要内容,如果未能解决你的问题,请参考以下文章

mysql sql优化和sql执行计划

使用 WHERE 子句中的过滤器优化 OUTER JOIN 查询。(查询规划器)

在 SQL 中动态构建 WHERE 子句,无需硬解析即可执行

PostgreSQL 中的 SQL JOIN - WHERE 子句中的执行计划与 ON 子句中的不同

Oracle 查询优化器是不是将*** where 子句应用于子查询或视图?

WHERE 谓词的顺序和 SQL 优化器