Почему запрос резко замедляется, если в предложении WHERE константа заменяется параметром (имеющим то же значение)? - PullRequest
11 голосов
/ 19 ноября 2010

У меня есть рекурсивный запрос, который выполняется очень быстро, если предложение WHERE содержит константу, но становится очень медленным, если я заменяю константу параметром, имеющим то же значение.

Запрос # 1- с константой

;WITH Hierarchy (Id, ParentId, Data, Depth)
AS
( SELECT Id, ParentId, NULL AS Data, 0 AS Depth
  FROM Test
  UNION ALL
  SELECT h.Id, t.ParentId, COALESCE(h.Data, t.Data), Depth + 1 AS Depth
  FROM Hierarchy h
       INNER JOIN Test t ON t.Id = h.ParentId
)
SELECT *
FROM Hierarchy
WHERE Id = 69

Запрос №2 - с параметром

DECLARE @Id INT
SELECT @Id = 69

;WITH Hierarchy (Id, ParentId, Data, Depth)
AS
( SELECT Id, ParentId, NULL AS Data, 0 AS Depth
  FROM Test
  UNION ALL
  SELECT h.Id, t.ParentId, COALESCE(h.Data, t.Data), Depth + 1 AS Depth
  FROM Hierarchy h
       INNER JOIN Test t ON t.Id = h.ParentId
)
SELECT *
FROM Hierarchy
WHERE Id = @Id

В случае таблицы с 50000 строк выполняется запрос с константойв течение 10 миллисекунд, а параметр с параметром работает в течение 30 секунд (в 3000 раз медленнее).

Невозможно переместить последнее предложение WHERE в определение привязки рекурсии, как мне хотелось быиспользовать запрос для создания представления (без последнего WHERE).У выбора в представлении будет предложение WHERE (WHERE Id = @Id) - мне это нужно из-за Entity Framework, но это уже другая история.

Может кто-нибудь предложить способ форсировать запрос № 2 (с помощьюпараметр) использовать тот же план запроса, что и запрос № 1 (с константой)?

Я уже пробовал играть с индексами, но это не помогло.

Если кто-то захочет, я могу опубликоватьопределение таблицы и некоторые примеры данных.Я использую SQL 2008 R2.

Заранее благодарю за помощь!

План выполнения - Запрос № 1 - с постоянной

alt text

План выполнения - Запрос № 2 - с параметром

alt text

Ответы [ 4 ]

5 голосов
/ 22 ноября 2010

Как предложил Мартин в комментарии к вопросу, проблема заключается в том, что SQL-сервер неправильно отбрасывает предикат из предложения WHERE - см. Ссылку в его комментарии.

В итоге я создал пользовательскую табличную функцию и использовал ее с оператором CROSS APPLY для создания представления.

Давайте посмотрим на само решение.

Определяемая пользователем табличная функция

CREATE FUNCTION [dbo].[TestFunction] (@Id INT)
RETURNS TABLE 
AS
RETURN 
(
    WITH
    Hierarchy (Id,  ParentId, Data, Depth)
    AS(
    SELECT Id, ParentId, NULL AS Data, 0 AS Depth FROM Test Where Id = @Id
    UNION ALL
    SELECT h.Id, t.ParentId, COALESCE(h.Data, t.Data), Depth + 1 AS Depth
        FROM Hierarchy h
            INNER JOIN Test t ON t.Id = h.ParentId
    )
    SELECT * FROM Hierarchy
)

View

CREATE VIEW [dbo].[TestView]
AS
SELECT t.Id, t.ParentId, f.Data, f.Depth
FROM
    Test AS t
    CROSS APPLY TestFunction(Id) as f

Запрос с постоянной

SELECT * FROM TestView WHERE Id = 69

Запрос с параметром

DECLARE @Id INT
SELECT @Id = 69
SELECT * FROM TestView WHERE Id = @Id

Запрос с параметром выполняется в основном так же быстро, как и запрос с константой.

Спасибо, Мартин, и за других!

1 голос
/ 19 ноября 2010

Вы должны использовать план плана , чтобы заморозить план, который вы хотите.

1 голос
/ 21 ноября 2010

Это может быть наихудшее предложение, но рассматривали ли вы возможность создания sproc для создания вашего запроса в виде строки и выполнения его с помощью sp_executesql?

Я ничего не знаю о поведении кэширования SQL, выполняемого sp_executesql,это было просто первое, что пришло мне в голову.

1 голос
/ 19 ноября 2010

Для вашего второго запроса попробуйте использовать OPTIMIZE FOR или OPTION (RECOMPILE) подсказка запроса , чтобы посмотреть, заставляет ли он его перекомпилироваться на основе предоставленного значения параметра.

...