Предложение WHERE не фильтруется в логическом порядке - PullRequest
2 голосов
/ 27 марта 2012

Я пишу общую хранимую процедуру поиска для поиска в таблице на основе множества фильтров, которые пользователь может выбрать в пользовательском интерфейсе (используя MS-SQL 2008).

Вот упрощенная версия:

CREATE PROCEDURE SearchAll
    @FirstName NVARCHAR(MAX) = NULL,
    @LastName NVARCHAR(MAX) = NULL,
    @Age INT = NULL
AS
    SELECT * 
    FROM persons 
    WHERE 
        (@FirstName IS NULL OR FirstName = @firstname)
        AND (@LastName IS NULL OR LastName = @LastName)
        AND (@Age IS NULL OR Age = @Age)

Похоже, что если я передам NULL @Age, производительность не снизится.Но, когда я тестирую с огромным количеством данных, я теряю отличную производительность!

Вот запросы, которые одинаковы по логике, но ОЧЕНЬ отличаются практически:

DECLARE @FirstName NVARCHAR(MAX) = NULL
DECLARE @Age INT = 23
------------First slow------------
SELECT * 
FROM persons 
WHERE 
    (@FirstName IS NULL OR FirstName = @firstname)
    AND (@Age IS NULL OR Age = @Age)
------------Very fast------------
SELECT * 
FROM persons 
WHERE 
    Age = @Age

Сделалпропустить точку?

Я знаю, что механизм SQL находит лучшее соответствие для индексов и ... (до выполнения запроса), но очевидно, что: @FirstName IS NULL и анализировать что-либо не нужно.

Я также протестировал ISNULL функцию в запросе (тот же результат).

Ответы [ 2 ]

5 голосов
/ 27 марта 2012

Запросы, содержащие эту конструкцию @variable is null or @variable = column, являются катастрофой производительности. Это связано с тем, что планы SQL создаются так, чтобы они работали для любого значения переменной. Подробное обсуждение темы, проблем и возможных решений см. Условия динамического поиска в T-SQL

0 голосов
/ 28 марта 2012

Проблема, как уже упоминалось, заключается в том, что план запроса будет построен для работы с любым значением переменных.Вы можете обойти это, построив запрос только с требуемым параметром, следующим образом:

CREATE PROCEDURE SearchAll
    @FirstName NVARCHAR(MAX) = NULL,
    @LastName NVARCHAR(MAX) = NULL,
    @Age INT = NULL
AS
BEGIN
    DECLARE @sql NVARCHAR(MAX), @has_where BIT
    SELECT @has_where = 0, @sql = 'SELECT * FROM persons '

    IF @FirstName IS NOT NULL
        SELECT @sql = @sql + 'WHERE FirstName = ''' + @FirstName + '''', @has_where = 1
    IF @LastName IS NOT NULL
        SELECT @sql = @sql + CASE WHEN @has_where = 0 THEN 'WHERE ' ELSE 'AND ' END + 'LastName = ''' + @LastName + '''', @has_where = 1
    IF @Age IS NOT NULL
        SELECT @sql = @sql + CASE WHEN @has_where = 0 THEN 'WHERE ' ELSE 'AND ' END + 'Age = ' + CAST(@Age AS VARCHAR), @has_where = 1

    EXEC sp_executesql @sql
END
...