Страницы: 1
RSS
PQ: LeftJoin таблицы и развертывание одной строки по условию соответствия ближайшей ранней дате., прошу помочь исправить код запроса
 
Доброго времени суток!

Проблема: в рабочем файле, после добавления кода из примера в конец запроса, скорость загрузки упала с 12 000 до 100 строк в секунду.

Собственно, сам код наколхозил, пока делал файл-пример для этого же сабжа, но в контексте "Как это сделать?".
То, что хочу получить, в запросе Sales2.
Прошу умельцев подсказать, в чем косяк, даже если я запорол что-то базовое.

Спасибо всем, кто отзовется!

З.Ы2.: Эта же задача решена на DAX в PowerBI, и мера, в расчете которой используются таксы, что я сейчас хочу сджойнить в PowerQuery, пересчитывается до 20 сек. на 5М строк.  
Изменено: genosser - 15.05.2020 12:07:48
 
Цитата
Ну, барин, ты задачи ставишь. Можно и за 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 - 15.05.2020 08:59:07
Вот горшок пустой, он предмет простой...
 
PooHkrd, спасибо, "боевые" данные загружаются со скоростью 6-10 к  строк в секунду. Т.к. я себя перехвалил, когда создавал тему (скорость была 12к/сек), то жить можно.
Одна вещь появилась, которой раньше не было: в строке, где отображается процесс загрузки, показывается постоянное обращение к справочнику курсов, который загружен как только подключение. А до добавления новых шагов этого не было: в строке отображались все джойны в начале, потом была непрерывная загрузка. Это ленивые вычисления из-за добавленной в конец группировки так шалят?
Попробовал шаги из Вашего решения перенести в середину запроса, до джойна курсов - и уже полчаса крутится "Загрузка данных" без счетчика загруженных строк. Когда решение в конце, с указанной выше скоростью весь запрос отрабатывается за 8 минут.
Я замотаюсь всю структуру в примере воспроизводить, но все ключи, что выложил в пример, на той стадии уже есть в таблице.
 
genosser, последний шаг запроса, обращающегося к справочнику курсов оберните в Table.Buffer(), возможно скорости еще прибавится.
Сделал еще один  вариант по другому алгоритму. Попробуйте, возможно будет быстрее.
Изменено: PooHkrd - 15.05.2020 22:44:13
Вот горшок пустой, он предмет простой...
 
PooHkrd, спасибо за помощь. Утром буду пробовать, сейчас уже не соображаю.
 
PooHkrd,
Спасибо! прирост 1,5-2k строк в сек.

Поможете разобраться?
Код
[...]
    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 - 18.05.2020 14:49:39
 
genosser, Table.ReplaceValue это функция итератор, она перебирает строки таблицы рассматривая их как записи, поэтому в отличие от Table.TransformColumns (которая работает со списками) в ней можно ссылаться на значения из других столбцов, кроме тех, которые мы трансформируем. Теперь по пунктам:
1. Там можно хоть матом написать, это значение в дальнейших вычислениях не используется.  :D
2. Функция в 4 аргументе это просто функция в которой должно быть 3 аргумента на входе и какое-то значение (вообще любое текст/таблица/бинарный файл, что угодно) на выходе, в данном случае на выходе получаем список дат. Далее соответствие:
в a передается текущее значение из столбцов перечисленных в 5 аргументе (в текущем случае значения столбца "ContractDate"), а потом это значение заменяется на результат вычисления функции для текущей строки.
в b передается значение из второго аргумента, в нашем случае это 0.
в c передается значение из третьего аргумента, в нашем случае это значение поля текущей записи [CD]
Как видите в дальнейшей формуле аргумент b не задействован, но написать туда что-то нужно, иначе Table.ReplaceValue ругаться будет.
Вообще изначально хотел так:
Код
Table.CombineColumns( 
    Custom1,
    {"ContractDate", "CD"}, 
    (x)=>List.Dates( x{0}, Number.From( (if x{1}=null then Date.From(DateTime.LocalNow()) else x{1}) - x{0} ), #duration(1,0,0,0) ), 
    "ContractDate")

результат полностью аналогичный, и столбец CD потом удалять не нужно, но в таком случае результирующий столбец получался последним в списке, а примененное решение очередность столбцов не меняет.
Изменено: PooHkrd - 18.05.2020 15:35:33
Вот горшок пустой, он предмет простой...
 
PooHkrd, это супер, благодарю.
 
PooHkrd, короче, я поторопился(
В рабочем справочнике Tax более 1 клиента, и запрос Tax  берет в столбец "CD" первую дату контракта клиента, следующего по списку. Сразу я этого не заметил.
Вроде сориентировался сам: сгруппировал таблицу Tax по клиентам и применил те же шаги к столбцу с типом table. Еще к таблице Tax в рабочем файле применил фильтр по году (ибо весь справочник после трансформации стал содержать 1М строк).
Скорость около 10 к строк в сек (устраивает).
Добавил новый пример.
Вопрос уже такой: как бы Вы это решили? Возможно ли это сделать более изящно?
Изменено: genosser - 18.05.2020 17:46:55
 
может как-то так
Код
let
    Sales     = Excel.CurrentWorkbook(){[Name="Sales"]}[Content][[ID Cust], [Date], [ID Merch], [Rev]],
    Tax       = Table.Buffer(Table.TransformColumnTypes(Excel.CurrentWorkbook(){[Name="Tax"]}[Content],{"ID БН",type number},"")),
    Prefix    = Table.TransformColumns(Tax,{"ID БН", each "БН "&Text.From(_,"")}),
    IDБН      = List.Buffer(List.Transform({1..List.Max(Tax[#"ID БН"])},each "БН "&Text.From(_,""))),
    Indexed   = Table.AddIndexColumn(Sales, "i", 0),
    Joined    = Table.NestedJoin(Indexed,"ID Cust",Prefix,"ID Cust","x"),
    Transform = Table.ReplaceValue(Joined,
                    each [Date],
                    0,
                    (a,b,c)=> let 
                        filtered = Table.SelectRows(a,each [ContractDate]<=b), 
                        grouped  = Table.Group(filtered, {"ID БН"}, 
                                       {"_", each Table.Max(_,"ContractDate"), type []}
                                   ),
                        totable  = Table.FromRecords(grouped[_]),
                        Typed    = Table.TransformColumnTypes(totable, {"Rate", Percentage.Type},""),
                        pivot    = Table.Pivot(totable, IDБН, "ID БН", "Rate")
                    in pivot,
                    {"x"}
                ),
    Expand    = Table.ExpandTableColumn(Transform, "x", IDБН),
    Typed     = Table.TransformColumnTypes(Expand, 
                    {{"Date",type date}}&
                    List.Zip({
                        IDБН&{"ID Cust","ID Merch","Rev"},
                        List.Repeat({type number},List.Count(IDБН)+3)
                    }),
                    ""
                ),
    Sorted    = Table.Sort(Typed,{{"i", Order.Ascending}}),
    RemoveCol = Table.RemoveColumns(Sorted,{"i"})
in
    RemoveCol
Изменено: Андрей Лящук - 18.05.2020 21:19:12
 
Андрей Лящук, спасибо большое. Извините, в запаре долго не было времени разобрать решение и отписаться.

В общем, очищенный от всяких прочих трансформаций запрос в рабочем файле работает в варианте из #11 со скоростью 22 000 строк/с, из #12 - 500 строк/с.
Благодарю всех за помощь!
Страницы: 1
Наверх