Необязательные параметры, план поиска индекса - PullRequest
2 голосов
/ 18 августа 2011

В своем операторе 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 к запросу, оптимизатор создаст более эффективный план, который использует «поиск».Он работает на моем сервере 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-сервера изменят план, как показано на рисунке.

Ответы [ 4 ]

1 голос
/ 19 августа 2011

См. Условия динамического поиска в T-SQL .

Подробно объясняются версии, где работает опция RECOMPILE, и альтернативы, где она не работает.

1 голос
/ 18 августа 2011

Ответ на комментарий к ответу Андреаса

Проблема в том, что вам нужны два разных плана.

  • Если @p1 = 1 тогда вы можете использовать SEEK для индекса.
  • Если, однако, @p1 IS NULL, это не поиск, по определению это SCAN.

Это означает, что когдаОптимизатор генерирует план До того, как узнает о параметрах, он должен создать план, который сможет реализовать все возможности.Только сканирование может удовлетворить потребности Оба @p1 = 1 И @p1 IS NULL.

Это также означает, что если план перекомпилируется в момент, когда параметрыизвестны, и @p1 = 1, план SEEK можно создать.

По этой причине, как вы упоминаете в своем комментарии, заявления IF решают вашу проблему;Каждый блок IF представляет отдельную часть проблемного пространства, и каждому может быть присвоен свой план выполнения.

1 голос
/ 18 августа 2011

Причина, по которой вы получаете сканирование, состоит в том, что предикат не будет закорачиваться, и оба оператора всегда будут оцениваться. Как вы уже заявили, он не будет работать с оптимизатором и принудительно сканировать. Хотя with recompile иногда кажется полезным, это не согласуется.

Если у вас большой стол, где поиск необходим, у вас есть два варианта:

  1. Динамический sql.
  2. Если операторы разделяют ваши запросы и, таким образом, создают отдельные планы выполнения (когда @p is null вы, конечно, всегда будете получать сканирование).
0 голосов
/ 18 августа 2011

Посмотрите на эту статью 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)`
...