2014-09-11 3 views
0

У меня есть T-SQL запрос (SQL Server 2008/2012), который использует следующую конструкцию:SQL Server: подзапросы в «логический» ИНЕК

WHERE (@param1 IS NULL OR column1 = @param1) 
AND (@param2 IS NULL OR column2 = @param2) 
AND (@param3 IS NULL OR column3 = @param3) 

Это прекрасно работает и автоматически оптимизирует любого из И 'ed частей предложения WHERE с параметрами NULL.

Но когда я делаю это же самое с подзапроса, оптимизатор, кажется, все-таки выполнить подзапросы, даже если парам является NULL:

WHERE 
    (@param1 IS NULL OR column1 IN (SELECT column 
            FROM table 
            WHERE column = @param1)) 
    AND (@param2 IS NULL OR column2 IN (SELECT column 
             FROM table 
             WHERE column = @param2)) 
    AND (@param3 IS NULL OR column3 IN (SELECT column 
             FROM table 
             WHERE column = @param3)) 

Мой вопрос, почему бы оптимизатор даже рассматривать «IN» SELECT, поскольку тот факт, что параметр имеет значение NULL, должен иметь «короткую замыкаемость» этой части запроса?

+0

Насколько я знаю, в SQL Server нет «короткого замыкания». Вам нужно использовать 'CASE', где вы можете добиться« короткого замыкания ». – cha

+0

Прочтите мой [этот ответ] (http://stackoverflow.com/questions/22978759/handling-select-condition-dynamically/22978868#22978868) для пример использования CASE – cha

+0

@cha: Я уверен, что первый пример кода работает так, как ожидалось; Я не изобретал это, но я использовал его в течение некоторого времени, и план выполнения доказывает ожидаемое поведение. Второй пример кода также работает, но план выполнения показывает, что подзапросы все еще выполняются. – eric

ответ

0

@ user640466: Я думаю, что если вы добавите подсказку запроса OPTION (RECOMPILE), плохая схема исчезнет, ​​потому что оптимизатор запросов должен оценить предложение IN, чтобы получить значение ProductID, тогда как в случае OPTION (перекомпилировать) это может понюхать значение времени выполнения. Pls см ниже рабочей копии из Adventure работы:

CREATE PROC MyTest 
@ProductID INT , 
@Name NVARCHAR(50), 
@ProductNumber NVARCHAR(50) 
AS 
SELECT * 
FROM Production.Product 
WHERE 
(ProductID IN (SELECT ProductID FROM Production.Product WHERE ProductID = @ProductID) OR @ProductID IS NULL) 
AND 
(Name IN (SELECT Name FROm Production.Product WHERE Name = @Name) OR @Name IS NULL) 
AND 
(ProductNumber IN (SELECT ProductNumber FROM Production.Product WHERE ProductNumber = @ProductNumber) OR @ProductNumber IS NULL) 
--OPTION (RECOMPILE) 
GO 

Если вы выполняете Proc: EXEC MyTest 1, NULL, NULL вы получите длинный план, однако изменить зр и раскомментируйте OPTION (RECOMPILE) вы получите хороший план.

+0

Я буду экспериментировать с этим, но даже в этом случае оптимизатор может легко избежать подзапроса даже без перекомпиляции; почему даже выполнить подзапрос, если логика OR делает результат подзапроса неуместным? – eric

Смежные вопросы