【问题标题】:Optional parameters, "index seek" plan可选参数,“索引搜索”计划
【发布时间】:2011-08-18 08:39:19
【问题描述】:

在我的 SELECT 语句中,我以如下方式使用可选参数:

DECLARE @p1 INT = 1  
DECLARE @p2 INT = 1  
SELECT name FROM some_table WHERE (id = @p1 OR @p1 IS NULL) AND (name = @p2 OR @p2 IS NULL)

在这种情况下,优化器会为实体生成“索引扫描”(而不是查找)操作,当提供的参数不是空值时,这不是最有效的操作。
如果我将 RECOMPILE 提示添加到查询中,优化器会构建更有效的计划,该计划使用“seek”。它适用于我的 MSSQL 2008 R2 SP1 服务器,这也意味着优化器可以构建一个仅考虑我的查询的一个逻辑分支的计划。
我怎样才能让它在我想要的任何地方使用该计划而无需重新编译?在这种情况下,USE PLAN 提示似乎不起作用。

下面是测试代码:

-- see plans  
CREATE TABLE test_table(  
    id INT IDENTITY(1,1) NOT NULL,   
    name varchar(10),    
    CONSTRAINT [pk_test_table] PRIMARY KEY CLUSTERED (id ASC))  
GO  
INSERT INTO test_table(name) VALUES ('a'),('b'),('c')  
GO  
DECLARE @p INT = 1  
SELECT name FROM test_table WHERE id = @p OR @p IS NULL  
SELECT name FROM test_table WHERE id = @p OR @p IS NULL OPTION(RECOMPILE)  
GO  
DROP TABLE test_table  
GO  

请注意,并非所有版本的 SQL Server 都会按照我显示的方式更改计划。

【问题讨论】:

    标签: sql sql-server optional-parameters sql-execution-plan


    【解决方案1】:

    您获得扫描的原因是谓词不会短路,并且始终会评估两个语句。正如您已经说过的那样,它不适用于优化器并强制扫描。尽管with recompile 有时似乎有所帮助,但并不一致。

    如果您有一张必须进行搜索的大桌子,那么您有两种选择:

    1. 动态sql。
    2. If 语句将您的查询分开,从而创建单独的执行计划(当@p is null 时,您当然会得到扫描)。

    【讨论】:

    • 感谢您的回复。是的,我知道其他解决搜索的方法 - 使用 IF、重构 SQL 等。但很遗憾,我无法为查询提供由优化器仅为此查询构建的计划......
    【解决方案2】:

    对安德烈亚斯回答的评论回复

    问题是您需要两个不同的计划。

    • 如果是 @p1 = 1,那么您可以在索引上使用 SEEK。
    • 但是,如果 @p1 IS NULL 不是搜索,根据定义,它是 SCAN。

    这意味着当优化器生成一个计划时在了解参数之前,它需要创建一个可以满足所有可能性的计划。只有一个 Scan 才能满足 Both @p1 = 1 @p1 IS NULL 的需求。

    这也意味着如果在参数已知的时候重新编译计划,并且@p1 = 1,则可以创建一个SEEK计划

    正如您在评论中提到的那样,这就是 IF 语句解决您的问题的原因;每个 IF 块代表问题空间的不同部分,并且可以为每个块指定不同的执行计划。

    【讨论】:

      【解决方案3】:

      Dynamic Search Conditions in T-SQL

      这全面解释了 RECOMPILE 选项有效的版本以及无效的替代方案。

      【讨论】:

        【解决方案4】:

        看这篇文章http://www.bigresource.com/Tracker/Track-ms_sql-fTP7dh01/ 看来您可以尝试使用提案解决方案:

        `SELECT * FROM <table> WHERE IsNull(column, -1) = IsNull(@value, -1)`
        

        `SELECT * FROM <table> WHERE COALESCE(column, -1) = COALESCE(@value, -1)`
        

        【讨论】:

        • 这与 OP 的逻辑不匹配。你需要WHERE column = IsNull(@value, column),这会产生非常不同的属性。
        猜你喜欢
        • 2015-11-06
        • 1970-01-01
        • 2015-01-18
        • 1970-01-01
        • 1970-01-01
        • 2023-04-08
        • 1970-01-01
        • 2018-05-06
        • 2016-05-29
        相关资源
        最近更新 更多