Проблема: в рабочем файле, после добавления кода из примера в конец запроса, скорость загрузки упала с 12 000 до 100 строк в секунду.
Собственно, сам код наколхозил, пока делал файл-пример для этого же сабжа, но в контексте "Как это сделать?". То, что хочу получить, в запросе Sales2. Прошу умельцев подсказать, в чем косяк, даже если я запорол что-то базовое.
Спасибо всем, кто отзовется!
З.Ы2.: Эта же задача решена на DAX в PowerBI, и мера, в расчете которой используются таксы, что я сейчас хочу сджойнить в PowerQuery, пересчитывается до 20 сек. на 5М строк.
Ну, барин, ты задачи ставишь. Можно и за 10 дён управиться!
Если я все правильно понял. 1. Исходником служат первые 4 столбца таблицы Sales и nаблица Tax. 2. Нужно в таблицу Sales по четкому соответствию в столбце ID Cust и при помощи интервального просмотра найти строку с датой договора ближайшей, но не превышающую дату в столбце Sales подтянуть из этой строки ID БН и значение Rate. 3. Потом все получившиеся ID БН свернуть в заголовки столбцов, а значениями сделать Rate без агрегирования. Я все правильно перевел?
PooHkrd, все верно. Ещё добавлю: в примере оставил один IDCust, для которого нет соответствия в таблице Tax - таких много: более половины. Null в столбцах БН* для таких клиентов устраивает, но как избежать вычислений для них, я быстро не придумал.
Накидал по-быстрому. Принцип там понятен, красивость сами наведете. Попробуйте скорость на боевых данных - отпишитесь. Если не очень, то есть еще одна идейка, чтобы без группировки обойтись. Если на уровне алгоритма, то дополнив справочник Tax теми ID Cust, которые в нем отсутствуют, и поставив для них Дату договора какую-то любую в далеком прошлом, скажем 1900 год, то группировки можно будет избежать и решить задачу только двойной сортировкой, но тогда это будет лишнее чтение с диска ваших 5 млн строк. В общем пробуйте.
PooHkrd, спасибо, "боевые" данные загружаются со скоростью 6-10 к строк в секунду. Т.к. я себя перехвалил, когда создавал тему (скорость была 12к/сек), то жить можно. Одна вещь появилась, которой раньше не было: в строке, где отображается процесс загрузки, показывается постоянное обращение к справочнику курсов, который загружен как только подключение. А до добавления новых шагов этого не было: в строке отображались все джойны в начале, потом была непрерывная загрузка. Это ленивые вычисления из-за добавленной в конец группировки так шалят? Попробовал шаги из Вашего решения перенести в середину запроса, до джойна курсов - и уже полчаса крутится "Загрузка данных" без счетчика загруженных строк. Когда решение в конце, с указанной выше скоростью весь запрос отрабатывается за 8 минут. Я замотаюсь всю структуру в примере воспроизводить, но все ключи, что выложил в пример, на той стадии уже есть в таблице.
genosser, последний шаг запроса, обращающегося к справочнику курсов оберните в Table.Buffer(), возможно скорости еще прибавится. Сделал еще один вариант по другому алгоритму. Попробуйте, возможно будет быстрее.
[...]
ReplacedValue = Table.ReplaceValue(
Custom1,
0,
each [CD],
(a,b,c)=> List.Dates( a,
Number.From( (if c=null then Date.From(DateTime.LocalNow()) else c) - a ),
#duration(1,0,0,0) ),
{"ContractDate"}),
[...]
1. Почему OldValue = 0? Ведь он же в четвертом шаге передается как a = StartDate, и each [CD] = c? 2. Функция, вложенная в Table.ReplaeValue четвертым аргументом, всегда забирает в свой первый аргумент OldValue, во второй NewValue? 3й аргумент (если есть) и далее просто дописываются руками? 3. И из этого следует, что нужно было записать (a,b,c)=>List.Dates(), чтобы редактор не ругался на "а" в первом аргументе List.Dates, но при этом List.Dates все равно может забрать себе в первый аргумент только OldValue из Table.ReplaceValue(), в которую он вложен? Что есть b? Это инкремент в List.Date? Если так, то почему не записываем (a,c,b)=>...? Либо вся конструкция (a,b,c)=> сообщает только то, что дальше будет функция с тремя аргументами, которая будет работать, как я предположил в п.2?
genosser, Table.ReplaceValue это функция итератор, она перебирает строки таблицы рассматривая их как записи, поэтому в отличие от Table.TransformColumns (которая работает со списками) в ней можно ссылаться на значения из других столбцов, кроме тех, которые мы трансформируем. Теперь по пунктам: 1. Там можно хоть матом написать, это значение в дальнейших вычислениях не используется. 2. Функция в 4 аргументе это просто функция в которой должно быть 3 аргумента на входе и какое-то значение (вообще любое текст/таблица/бинарный файл, что угодно) на выходе, в данном случае на выходе получаем список дат. Далее соответствие: в a передается текущее значение из столбцов перечисленных в 5 аргументе (в текущем случае значения столбца "ContractDate"), а потом это значение заменяется на результат вычисления функции для текущей строки. в b передается значение из второго аргумента, в нашем случае это 0. в c передается значение из третьего аргумента, в нашем случае это значение поля текущей записи [CD] Как видите в дальнейшей формуле аргумент b не задействован, но написать туда что-то нужно, иначе Table.ReplaceValue ругаться будет. Вообще изначально хотел так:
результат полностью аналогичный, и столбец CD потом удалять не нужно, но в таком случае результирующий столбец получался последним в списке, а примененное решение очередность столбцов не меняет.
PooHkrd, короче, я поторопился( В рабочем справочнике Tax более 1 клиента, и запрос Tax берет в столбец "CD" первую дату контракта клиента, следующего по списку. Сразу я этого не заметил. Вроде сориентировался сам: сгруппировал таблицу Tax по клиентам и применил те же шаги к столбцу с типом table. Еще к таблице Tax в рабочем файле применил фильтр по году (ибо весь справочник после трансформации стал содержать 1М строк). Скорость около 10 к строк в сек (устраивает). Добавил новый пример. Вопрос уже такой: как бы Вы это решили? Возможно ли это сделать более изящно?
Андрей Лящук, спасибо большое. Извините, в запаре долго не было времени разобрать решение и отписаться.
В общем, очищенный от всяких прочих трансформаций запрос в рабочем файле работает в варианте из #11 со скоростью 22 000 строк/с, из #12 - 500 строк/с. Благодарю всех за помощь!