Сумма (…) OVER (ORDER BY…) ввиду плохой производительности - PullRequest
0 голосов
/ 23 января 2019

У меня есть определенное представление, которое перечисляет транзакции вместе с промежуточной суммой, что-то вроде

CREATE VIEW historyView AS
    SELECT
        a.createdDate,
        a.value,
        m.memberId,
        SUM(a.value) OVER (ORDER BY a.createdDate) as runningTotal,
        ...many more columns...
        FROM allocations a
        JOIN member m ON m.id = a.memberId
        JOIN ...many joins...

В самых больших таблицах, на которые смотрит этот запрос, ~ 10 миллионов строк, но в среднем, когда представление являетсязапросит только несколько десятков строк.

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

SELECT h.createdDate, h.value, h.runningTotal
    FROM historyView h
    WHERE member.username = 'blah@blah.com'

... производительность ужасна.Два плана запроса очень разные - в первом случае он в значительной степени идеален, но во втором случае загружается множество сканов и считываются сотни тысяч / миллионы строк.Это очевидно, потому что фильтр на элементе запускается последним после того, как все остальное было сделано, а не сразу при запуске.

Если я удалю предложение SUM(x) OVER (ORDER BY y), эта проблема исчезнет.

Могу ли я что-нибудь сделать, чтобы предложение SUM(x) OVER (ORDER BY y) не нарушило план запроса?

Ответы [ 2 ]

0 голосов
/ 23 января 2019

Это потому, что вероятно есть индекс m.username.Когда дело доходит до настройки запроса, это требует проб и ошибок.
При использовании оконных функций следует учитывать концепцию индекса POC - просто выполните поиск в Google (Ицик Бен-Ган также имеет хорошие ссылки на это).
Из книги «Высокопроизводительный T-SQL с использованием оконных функций ':

В отсутствие индекса POC план включает в себя итератор Sort, а с большими входными наборами он может быть довольно дорогим.Сортировка имеет сложность N * LOG (N), которая хуже линейной.Это означает, что чем больше строк, тем больше вы платите за ряд.Например, 1000 * LOG (1000) = 3000 и 10000 * LOG (10000) = 40000. Это означает, что увеличение числа строк в 10 раз приводит к увеличению объема работы в 13 раз, и чем дальше вы работаете, тем хуже.

Вот ссылка ссылка , чтобы начать работу с оконными функциями и индексами.

0 голосов
/ 23 января 2019

Одно из решений моей проблемы - сообщить оптимизатору запросов, что безопасно фильтровать перед запуском оконной функции с помощью PARTITION 'по этому свойству.Изменение представления:

CREATE VIEW historyView AS
    SELECT
        a.createdDate,
        a.value,
        m.memberId,
        SUM(a.value) OVER (PARTITION BY m.username ORDER BY a.createdDate) as runningTotal,
        ...many more columns...
        FROM allocations a
        JOIN member m ON m.id = a.memberId
        JOIN ...many joins...

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

...