Оконные функции вызывают ужасный план выполнения при вызове из представления с внешним параметризованным предложением 'where'

10

У меня была эта проблема давным-давно, я нашел обходной путь, который подходил мне, и забыл об этом.

Но теперь есть такой вопрос о SO, поэтому я хочу поднять эту проблему.

Есть представление, которое соединяет несколько таблиц очень простым способом (заказы + строки заказа).

При запросе без whereпредложения представление возвращает несколько миллионов строк.
Тем не менее, никто никогда не называет это так. Обычный запрос

select * from that_nasty_view where order_number = 123456;

Это возвращает около 10 записей из 5 миллионов.

Важная вещь: представление содержит оконную функцию, rank()которая разделена точно полем, с помощью которого представление всегда запрашивается:

rank() over (partition by order_number order by detail_line_number)

Теперь, если это представление запрашивается с литеральными параметрами в строке запроса, точно так, как показано выше, он мгновенно возвращает строки. План выполнения в порядке:

  • Поиск индекса по обеим таблицам с использованием индексов order_number(возвращает 10 строк).
  • Расчет окон по возвращенному крошечному результату.
  • Выбор.

Однако, когда представление вызывается параметризованным способом, все становится неприятным:

  • Index scanна всех столах игнорируя индексы. Возвращает 5м строк.
  • Огромное присоединение.
  • Расчет окон по всем partitions (около 500 тыс. Окон).
  • Filter взять 10 рядов из 5м.
  • Выбрать

Это происходит во всех случаях, когда задействованы параметры. Это может быть SSMS:

declare @order_number int = 123456;
select * from that_nasty_view where order_number = @order_number;

Это может быть клиент ODBC, например Excel:

select * from that_nasty_view where order_number = ?

Или это может быть любой другой клиент, который использует параметры, а не SQL-конкатенацию.

Если оконная функция удалена из представления, она работает идеально быстро, независимо от того, запрашиваются ли ее параметры.

Мой обходной путь состоял в том, чтобы удалить нарушающую функцию и повторно применить ее на более позднем этапе.

Но что дает? Действительно ли это ошибка в работе SQL Server 2008 с оконными функциями?

GSerg
источник
порядковый номер является первичным ключом? Типы данных столбца и параметра совпадают?
ГБН
order_numberне первичный ключ. Это int not nullс некластеризованным индексом в обеих таблицах.
GSerg
5
В SQL Server 2005 были проблемы с использованием предикатов в этой области. Я думал, что они были исправлены сейчас. Кстати, ваш пример TSQL использует переменную, а не параметр. Помогает ли добавление OPTION (RECOMPILE)?
Мартин Смит
1
@GSerg - То есть на плохом плане с последним фильтром в нем примерно 5 миллионов строк и примерно 10 строк, соответствующих фактическому? Если так, то, может быть, проблема с предикатом все еще не решена полностью.
Мартин Смит

Ответы:

5

Это, кажется, давняя проблема, которая постоянно обновляется в той или иной форме и все еще присутствует в SQL Server 2012.

Некоторые посты обсуждают это

Все текущие версии SQL Server до 2012 года включительно не могут использовать фильтр в группе секционирования после проекта последовательности для параметризованного предиката, кроме случаев option(recompile)использования (если 2008+).

Альтернативой recompileподсказке было бы переписать запрос для использования параметризованного встроенного TVF, как предложено @ a1ex07)

Мартин Смит
источник
Так же было и в SQL Server 2014
Guillaume86
3

Я бы попробовал заменить представление табличным udf. Таким образом, он будет сначала фильтровать записи, а затем применять оконную функцию. Эта функция может принимать параметр таблицы, так что вы можете передать order_numberв нее несколько значений

a1ex07
источник
Еще один обходной путь, да. Я не мог этого сделать, потому что не все клиенты могли использовать табличную функцию.
GSerg
Почему? Я не уверен на 100%, но я думаю, что все, что вам нужно, это немного изменить запрос на что-то вродеSELECT * FROM my_funct(12345)
a1ex07
Одним из требований было использование запроса конечными пользователями, использующими Excel (то есть MS Query), и MS Query не позволит вам сделать это, по крайней мере, в версиях до 2003 года.
GSerg
it will filter records first, and then apply window functionэто неверно. Нет никакого детерминированного заказа к исполнению
Ремус Русану