Добрый день, уважаемые знатоки! Подскажите, пожалуйста, в чем может быть ошибка: Использую меру для подсчета нарастающего итога по продажам (sales) в разрезе продукта (truck_id). В таблице sales около 300.000 строк (продажи), в неё добавлен столбец [Index] Мера вычисляет очень долго - порядка 40-60 секунд. Если поставить в 3-й строке вместо VAR car_index = SELECTVALUE(sales[Index]) (использовал и MAX(sales[Index]) - разницы нет) любое число (пробовал даже 300000) - работает быстро. Загвоздка именно в использовании индекса. Может сможете подсказать, как можно ускорить её работу?
Текст меры: sales_runn_total = VAR cur_truck_id = SELECTVALUE(sales[truck_id]) VAR cur_index = SELECTVALUE(sales[Index]) VAR run_total = CALCULATE(SUM(sales[Стоимость]), FILTER(ALL(sales), sales[Index]<=cur_index)) RETURN SWITCH(TRUE(), ISINSCOPE(sales[Index] && cur_truck_id <> BLANK(), run_total,
Добрый день! Помогите, пожалуйста, решить вот такую задачу: У меня три таблицы: Партии, Товары, Продажи. В таблице Партии информация о закупленных партиях товара (IDпартия). Есть таблица связи товара по IDПартия. И есть таблица продаж с товарными позициями (IDтовар). Партия была закуплена за определенную сумму (Закупка) с доп.расходами (ДопРасходы) в определенную дату (ДатаПоставки). Партия поступает в продажу, и товар начинает продаваться (в таблице Продажи есть количество, стоимость и дата продажи конкретного IDТовара).Себестоимость партии высчитывается легко, прибыль тоже. Нужно рассчитать рентабельность в %% и окупаемость каждой партии в днях. У меня возникла проблема с окупаемостью - мера считает оооочень долго - может подскажете, в чем ошибка и как можно ускорить ее работу?
Меру для подсчета окупаемости использую такую: breakeven_days_new = VAR id = MAX(Партии[IDПартия]) VAR items_table = SELECTCOLUMNS(FILTER(ALL(Партии), Партии[IDПартия] = id), [IDПартия]) VAR sale_table = FILTER(ALL(Продажи), Продажи[IDТовара] in items_table) VAR sales_table = FILTER(ADDCOLUMNS(sale_table, "run_total", VAR ind = [Index] VAR sale_table_small = FILTER(sale_table, [Index]<ind) RETURN SUMX(sale_table_small, [Стоимость]) ), [run_total]>='Партии'[СебестоимостьПартии]) VAR date_diff = DATEDIFF( MIN(Партии[ДатаПоставки]), MINX(sales_table, [ДатаПродажи]), DAY ) RETURN date_diff
Добрый день! Помогите, пожалуйста, решить вот такую задачу: Есть 3 таблицы: Партии, Товары, Продажи. В таблице Товары информация по закупленным партиям товаров (IDпартии), В таблице Товары - связь между партией и проданной единицей товара. В таблице Продажи - проданные товарные позиции (IDтовар) с датой, количеством и стоимостью. Партия была закуплена за определенную сумму (Закупка) с доп.расходами (ДопРасходы) в определенную дату (ДатаПоставки). Партия поступает в продажу, и товар начинает продаваться (таблица Продажи). Себестоимость партии высчитывается легко, прибыль тоже. Нужно рассчитать рентабельность в %% и окупаемость каждой партии в днях. У меня возникла проблема с окупаемостью - мера считает оооочень долго - может подскажете, в чем ошибка и как можно ускорить ее работу?
Меру для подсчета окупаемости использую такую: breakeven_days_new = VAR id = MAX(Партии[IDПартия]) VAR items_table = SELECTCOLUMNS(FILTER(ALL(Партии), Партии[IDПартия] = id), [IDПартия]) VAR sale_table = FILTER(ALL(Продажи), Продажи[IDТовара] in items_table) VAR sales_table = FILTER(ADDCOLUMNS(sale_table, "run_total", VAR ind = [Index] VAR sale_table_small = FILTER(sale_table, [Index]<ind) RETURN SUMX(sale_table_small, [Стоимость]) ), [run_total]>='Партии'[СебестоимостьПартии]) VAR date_diff = DATEDIFF( MIN(Партии[ДатаПоставки]), MINX(sales_table, [ДатаПродажи]), DAY ) RETURN date_diff
Уважаемые коллеги, доброго времени суток! Столкнулся с такой проблемой - при загрузке таблицы из потока в desktop приложение происходит изменение общего количества строк, а точнее задвоение/затроение и т.д. некоторых из них, не всех. Никаких преобразований при этом в десктопе не делаю. Может кто-то подскажет, в чем может быть проблема и как её решить?
Alex, ну изначально стоит задача посчитать остаток товара на каждый конкретный день внутри периода. Соединить календарь с таблицей закупок возможности нету, можно правда использовать неактивную связь. Переменные это для других расчетов, Вы правы, они в данном куске кода не используются)
Уважаемые коллеги, помогите решить следующую проблему: у меня есть таблица с закупками и таблица-календарь, они не связаны. Мне нужно для каждой даты рассчитать количество закупленного товара (по конкретному id номенклатуры) с первой даты до каждой даты включительно т.е. по сути сделать нарастающий итог на дату. На первом скрине получается создать таблицу, содержащую даты и количество товара на каждую дату, но там я не использую фильтр по дате, т.е. ADDCOLUMNS считает на каждую дату все закупки. Формула вот такая:
На втором скрине собственно ошибка - я не могу обратиться к виртуальному столбцу [Date] - мера его не видит, т.е. вроде таблица создана функцией SUMMARIZE , как учат везде я обернул ее в ADDCOLUMNS, но внутренний CALCULATE наглухо отказывается обращаться к столбцу [Date] и фильтровать все поступления до конкретной даты:
te1n, супер, я есть в той группе, но почему то там не загуглил , виноват, спасибо, супер четкое и понятное объяснение!)) действительно это существенно расширяет варианты использования функции!!)) Alien Sphinx, te1n, еще раз спасибо за потраченное время, изучаю , тестирую, отпишусь по результату!) Даже не всегда понятный код от Гуру , дает пинок молодым подаванам, как я, изучать и понимать )
написал: почитайте справку по этой функции на сайте Майкрософта.
изучил, там не густо), есть еще два аргумента, optional occurrence as nullable number - понятно, это по всей видимости сколько возвращать результатов, если совпадений больше 1 optional equationCriteria as any - это я так понимаю по аналогии с другими функциями, параметр определяющий степень совпадения, но как его в данном случае использовать там ни слова) ну буду дальше гуглить ) спасибо за помощь))
Цитата
написал: Не супер быстро, но терпимо. И List.Generate / Accumulate тут не помогут, кмк
P.S. к стати на одном из сайтов прочитал что List.PositionOf на самом деле это и есть List.Generate "под капотом") не в качестве сомнений в вашем авторитете, я просто случайно наткнулся пока гуглил вашу магию с переменными))
Here is the M code behind the List.PositionOf function:
Код
(list as list, item as any) =>if list = null then nullelse List.Generate(() => [i = 0, found = false],each [i] <= List.Count(list) - 1 and not [found],each [i = [i] + 1, found = list{i} = item],each [i])
Alien Sphinx, спасибо за помошь, в целом логика понятна, и вобщем она должна работать, буду сейчас проверять на скорост и точность. Если можно пояснение по одному месту кода
вот тут мне казалось что List.PositionOf принимает всего два аргумента, а тут какая-то магия с объявлением переменных, можете пояснить ?)) Как работает объявление переменных в функциях Table.AddColumn, Table.Group и прочих итераторах мне более или менее понятно, а вот тут не очень, что они получают в качестве значений? te1n, ваш код еще не смотрел, буду завтра разбираться , спасибо за помощь и отклик!
Вот столкнулся с такой задачей, для меня она не тривиальная, но я честно гуглил и готовых решений не нашел, суть такая что есть база клиентов интернет магазина, 35.000 номеров, есть спарсенная при помощи PQ база кодов операторов по регионам, при помощи нее сопоставляю кусок номера (не всегда одинаковой длины) можно определить точно город регистрации сим карты. Сложность в том что кода из 3 первых цифр номера, префикса недостаточно, есть масса номеров, которые распределены между разными регионами, т.е. если номер PPP-NNNNNNN, то номера с префиксом PPP могут быть например Билайн тут же Мегафон, и тут же еще и разные регионы внутри одного оператора, поэтому где то совпадает PPP-N где-то PPP-NN и так далее.
Я саму задачу решил тупым джонойном выделил префикс каждого номера, сджойнил табличку всех возможных вариантов кодов к этому префиксу и далее тупым перебором нашел соответствия , при каждой итерации я отрезал от искомого номера такой кусок впереди, который будет равен по длине тому что есть в базе кодов операторов. Затем просто фильтрация, все решено, но остались вопросы, вот кусок этого решения
Так же удалось решить при помощи List.Transform, сджойненную таблицу напротив каждого номера, я пытался обработать, но решение очень медленное, я так и не дождался загрузки в отчет ни разу
Оба решения не изящные, в рамках тренировки и расширения опыта я хотел бы попробовать решить его при помощи List.Generate или List.Accumulate или каким-либо еще способом в рамках PQ можно в рамках PBI. Буду очень благодарен за пинок, в сторону быстрого решения что называется по Фэн Шую))
P.S. К стати решить этот же вопрос в Power BI было бы интересно, сравнить, что быстрее работает, правда и текущее решение в PQ работает весьма быстро, только в моем случае приходится хранить еще одну таблицу в 200.000 строк и она будет расти, а по сути в ней просто усеченные данные из основной, а вот мера или вычисляемый столбец в основной таблице, были бы более рациональным решением ))
написал: Константин Иванов, чем Вас не устроил вариант из сообщения #8 с локальной группировкой?
Добрый вечер)
Vladimir Ch, Вы верно написали, что надо конкретизировать ТЗ, да Ваша формула прекрасно считает для случая если надо просто взять период 10 дней "назад" и посчитать уникальные ID. Но она не отбрасывает лишние активации, т.е. если я 01.01.2023 по какому-то конкретному ID получаю статус активен и он как бы первый, первая активация, то далее я например если получаю 05.01.2023 снова "Активен", то я его должен проигнорировать, с этим Ваша формула справляется, все ок, а вот 10.01, 11.01, 12.01, 13.01, 14.01, 15,01 Ваша формула будет видеть активацию 05.01, а на самом деле эта активация лишняя, ее не надо считать вообще. surkenny, по Вашей формуле в PBI тоже самое - она считает корректно , но спотыкается на вот таких кейсах как я описал выше. Ваш код для PQ я тоже проверил и вот он дает верный результат, но у меня есть несколько вопросов по коду, если Вам не сложно прокомментить, буду очень благодарен, если времени нет, то и на том спасибо за работающее решение!)
1. Зачем мы добавляем столбец типа date в дополнение к datetime, я проверил все итак корректно работает, более того сортировка по datetime дает более точный результат, ведь в один день может прийти на один и тот же ID один и тот же статус несколько раз, лучше оставить именно ту строку которая была первой или есть какой-то скрытый смысл ?
2. { { "first row", ( t ) => Table.FirstN ( t, 1 ), type table } }, вот это место - тут в функции формирования нового поля, вы объявляете переменную, вопрос почему именно так, не проще написать Table.FirstN ( _, 1 ) ? вопрос без подвоха я действительно не понимаю )
3. ( s, c ) => Number.From ( c[id] <> s[id] or c[statusdate] > Date.AddDays ( s[statusdate], 9 ) ) ) а вот тут вообще какая-то магия )) но после изучения особенностей 5 го аргумента Table.Group все встало на свои места, особенно понравилось, что нигде в мануалах Microsoft ничего не написано конкретно как это работает и что он туда может передавать исходную строку таблицы и далее на основании этого строить агрегированную ... но нашлась англ статья где это описано, но ушло немало усилий для понимания, честно признаться ))
4. combine = Table.Combine ( group[first row] ) - вот тут совсем непонятно, достаточно удалить столбец "first row" и собственно таблица уже отфильтрована как надо, мне показалось, что на больших числах это будет быстрее чем Table.Combine?
Хочу еще раз всех поблагодарить surkennyVladimir Ch, !) Прошу прощения на за уход по-английски, и так тут бесплатно помогают, поэтому всем спасибо !
surkenny, ну вопрос как бы был решен, однако не все так радужно обнаружилась ошибка. Сейчас попытаюсь объяснить , нам надо посчитать количество активаций, имея ввиду что активации после "правильной" , которую надо считать, которые внутри 10 дневного периода считать не надо. Я на самом деле сам написал решение похожее на Ваше, когда формула смотрит на период 10 дней назад от каждой строки внутри каждого ID и смотрит были ли там активации и оказалось, что это некорректно. Особенно если хотим для каждой строки приходящих статусов понять надо ее считать или это ошибочная активация. Т.е. если считать общее количество за период это работает, а вот когда надо построчно прописать вот тут не работает. Вот кратенько пример 01.01.2023 мы активировались, 05.01.2023 мы получили еще один статус "Активно", затем 12.01.2023 снова и вот первый статус отрабатывает ок, второй статус тоже, система смотри как бы назад и видит активация 01.01.2023 и пропускает активацию 05.01.2023 , не считает ее, но вот на 12.01.2023 система спотыкается - она "смотрит" назад и тоже в 10 дневном периоде видит активацию и не считает, но на самом деле ее считать нужно, т.к. 05.01.2023 активация была ошибочной. Я наваял тут в Power Query такой код - базово он работает - как бы "запоминает" последнюю значимую активацию и сравнивает дату не с предыдущей в радиусе 10 дней, а ищет именно "правильную активацию" и если такой нет то он ткущую считает значимой и "запоминает" строку последней правильной\значимой активации что-бы потом сравнивать именно с ней, а не с любой предыдущей. Вроде все работает, однако на таблицу 250.000 строк, система тупо висит часами и не в состоянии посчитать. Как тоже самое сделать быстрее в Power Query или как это быстро сделать в Power BI ума не приложу , буду очень благодарен за помощь, заранее спасибо, файлик обновил и приложил. А вот мой код в Power Query
let
Source = Sql.Database("192.168.1.33", "bi"){[Schema="dbo",Item="Statuses"]}[Data], Filter = Table.SelectRows(Source, each ([status] = "Активно")and(([id]="000000072")or([id]="000000069")or([id]="000000070"))), Sort = Table.Sort(Filter, {{"id", Order.Ascending},{"statusdatetime", Order.Ascending}}), Index = Table.Buffer(Table.AddIndexColumn(Sort, "Index", 0, 1, Int64.Type)), Count = Table.RowCount(Index), Generate = List.Generate( ()=>[i=0, flag = 0, real=true], each [i] < Count, each [i=[i]+1, flag = if Index[id]{i}<>Index[id]{i-1} then i else if Index[statusdatetime]{i}<Date.AddDays(Index[statusdatetime]{[flag]},30) then [flag] else i, real = if Index[id]{i}<>Index[id]{i-1} then true else if Index[statusdatetime]{i}<Date.AddDays(Index[statusdatetime]{[flag]},30) then false else true ] ), Table = Table.FromList(Generate, Splitter.SplitByNothing(), null, null, ExtraValues.Error), Expand = Table.ExpandRecordColumn(Table, "Column1", {"i", "flag", "real"}, {"i", "flag", "real"}), Result = Table.Join(Index,"Index",Expand, "i", JoinKind.LeftOuter) in Result
написал: почему же виртуальной, физической в БД. Даты не берутся из таблицы фактов, потому что там могут быть пропуски (отсутствующие даты, например, выходные) Из-за чего расчеты пользователей будут неверны. При этом MAX и MIN в данном случае не имеет значения, это просто конкретная дата календаря, от которой отталкиваемся далее и мера производит свои расчеты.
т.е. мера сама "перебирает" дату если не наложатся внешние фильтры и например не будет выбран какой-то диапазон дат или конкретная дата , верно?
Просто я пока вот тут связь не могу уловить, простите мне мои вопросы )) Мне казалось переменные они объявляются и все, а тут получается не все так однозначно ?)
Vladimir Ch, да я читаю, изучаю, форумы, книги, инструкции и видеокурсы, в Power Query я вроде чуть получше погрузился, а вот Power BI имеет немного другой концепт, в целом он понятен, но вот в контекстах пока честно признаться плаваю )) хотя чем контекст для CALCULATE отличается от контекста для FILTER базово мне понятно )) Спасибо больше , если Вам не сложно посмотите я отредактировал свой пост, Вы были правы по поводу повторений, я подготовил очищенную таблицу , но сделал это в Power Query и учитывая что там 250.000 строк и она постоянно растет, вопрос проиводительности актуален ))
написал: Из описания неясно, по какому алгоритму нужно выкидывать ошибки "Активен - неактивен". Если это имеет значение, Вы должны продумать все возможные комбинации и описать алгоритм действия.
Добрый день.
На сама деле подсчет будет неточным, тут вы правы, т.к. будут дубли из-за повторных активаций, по сути надо еще убрать предварительно в Power Query активации внутри 10 дневных отрезков начиная с первой, т.е. по каждому ID взять первую активацию, выбросить все что активировалось <10 далее и получить "чистую" таблицу, только с теми активациями которые нужно считать. Я это сделал, правда не очень быстро получилось, но работает, елси подскажете быстрый способ то буду благодарен, все равно в Power Query или в Power BI.
Если можно, по коду получить пояснения , что-бы я так сказать больше с такими глупостями не постил сюда темы ))
Предварительно надо иметь активную связь между синтетической таблицей дат Calendar и таблице фактов (изменений статусов)? var current_day = MAX('Calendar'[Date]) - это мы берем максимальную дату из какой-то виртуальной таблицы дат, это формула судя по всему будет делать по строкам, т.е. каждый поход дата будет меняться ? var previous_day = current_day - 10 это сдвиг от той же даты на 10 , опять же я не силен но нигде не указано что это именно 10 дней а не месяцев или лет, это потому что таблица дат в предыдущей переменной по дням составлена или почему ? var rezult = CALCULATE( DISTINCTCOUNT([id]); - тут понятно, подсчет уникальных значений по столбцу id FILTER( ALL('Calendar'); - тут мы берем таблицу дат и снимаем для сначала с нее контекст , т.е. все фильтры, верно? 'Calendar'[Date]>previous_day && 'Calendar'[Date]<=current_day - тут мы задаем промежуток и получаем кусок той самой таблицы ограниченный нужным промежутком ); 'Таблица1'[status] = "Активен" - тут понятно, дополнительный фильтр в CALCULATE что бы получать только статус Активен, т.к. эта функция сначала вычисляет фильтры то для подсчета мы уже получим отфильтрованную таблицу ) return rezult
Добрый день, пытаюсь посчbтать в DAX вот такую вещь. Есть таблица изменения состояний с активного на неактивное, по идее активность длится 10 дней , то есть я вижу ID, в таблице и вижу что статус "Активен" значит он теперь будет активен 10 дней, правда внутри могут быть сбои, т.е. ID активировался 01.01.2022, 03.01.2022 может прийти что он "Неактивен", потом снова 04.01.2022 наприме, "Активен". Это технический сбой, мне нужно его игнорировать. Задача на каждый день, мерой (что бы потом можно было фильтровать по связанным таблицам) посчитать сколько было активных ID всего в этот день, но именно сумму всех активных на день, а не только тех которые стали активными в этот день, т.е. сумму всех активных на этот день включая все те что стали активными 1-2-3 и до 10 дней назад. По сути мне надо взять предыдущие 10 дней, из большой таблицы статусов, взять внутри нее все ID которые пришли со статусом "Активен", хотя бы один раз, это будет означать, что я получил все ID которые могли быть активны на эту дату, все что было раньше не может быть активным, так как макс срок активности 10 дней, все что было позже, разумеется, не анализируем, оно для текущей даты еще не наступило, сгруппировать, что-бы убрать ошибочные двойные, тройниые и проч активации и я должен по идее получить сумму всех ID которые за период пред 10 дней были активированы, т.е. получу сумму активных ID на этот день.
Делаю это мерой, она прекрасно считает все, что было активировано, но когда я раскидываю ее по датам она упорно не ссумирует, а считает именно активации в этот день, а предыдущие 9 игнорирует )) как ее заставить ссумировать или мера вообще так делать не умеет?
В PQ задача решилась несложно, но получивщуюся таблицу уже не отфильтровать связанными табличками. Возможно эту меру надо городить в отдельно таблице где не повторяются даты? тогда она сможет аккумулировать на каждую дату, надо тогда связь между ними делать?
surkenny, да, приношу свои извинения, код работает как надо и если отсортировать затем продажи обратно, все будет гут. Но я вот написал, что понял как код работает, при ближайшем рассмотрении, вынужен признать, нет )) прошу Вашей помощи: то, что Table.Group передает кусок таблицы сгруппированный по столбцам (в нашем случае по одному столбцу "ProductID") это я понял и легкомысленно решил, что далее все просто, но вот что я не понял это 1. Верно ли я понимаю, что далее после столбца группировки объявляется функция ( t ) => , разве нельзя просто там разместить List.Generate? 2. Если я все верно прочитал, то там после функции стоит [generate], вот этого я не понял совсем )) 3. Самое, пожалуй главное, как мы указываем функции List.Generate, что именно этот сгруппированный, отфильтрованный кусок таблицы, верно ли я понимаю, что это происходит на моменте recs = List.Buffer ( Table.ToRecords ( t ) ) т.е. на моменте объявления функции собственно мы и получаем что t = кусок этой исходной таблицы ?
Я очень прше прощения за вопросы, елси они покажутся дурацкими, но мне действительно непонятно )) заранее спасибо за потраченное на меня время!
[URL=#]?[/URL] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 let data = Excel.CurrentWorkbook(){[ Name = "data" ]}[Content], typed = Table.TransformColumnTypes ( data, { { "Date" , type datetime }, { "Price" , type number }, { "RN" , Int64.Type } } ), sort = Table.Sort ( typed, { { "ProductID" , Order.Ascending }, { "Date" , Order.Ascending } } ), group = Table.Group ( sort, { "ProductID" }, { { "recs" , ( t ) => [ recs = List.Buffer ( Table.ToRecords ( t ) ), generate = List.Generate ( () => [ i = 0, sum = recs{i}[Price], result = Record.AddField ( recs{i}, "Sum" , sum ) ], each [i] < List.Count ( recs ), each [ i = [i] + 1, sum = [sum] + recs{i}[Price], result = Record.AddField ( recs{i}, "Sum" , sum ) ], each [result] ) ][generate], type {record} } }, GroupKind.Local ), toTable = Table.FromRecords ( List.Combine ( group[recs] ) ) in toTable
surkenny, работает очень быстро, как работает я тоже вобщем-то разобрался, но только делает не совсем то, что я описывал, данный код считает нарастающий итог по продукту, а мне нужен нарастающий итог по продукту с учетом даты или индекса продажи т.е. мне нужны все продажи до и включая текущую, не не включая последующие, можно опираться на дату но лучше на столбец RN что бы можно было точно понимать какие продажи были до, а какие после текущей.
[URL=#]?[/URL] 1 2 3 4 5 6 7 8 9 10 11 12 13 let Source = Sql.Database( "Server" , "Database" ), sql = Value.NativeQuery( Source, "SELECT *, SUM(Price) OVER (PARTITION BY ProductID ORDER BY Date ) as RT FROM dbo.Table", null, [EnableFolding = true] ) in sql
Server, Database, Table (и схему, если не dbo ) подставляете свои. Вместо * можете выбрать только интересующие столбцы. Потестил с SQL Server 2019, работает. Если в Вашей базе данных нет оконок, можете посмотреть альтернативы .
Спасибо громадное, я не очень силен в SQL, но я примерно понял как это работает, но только не вижу отбора на каждую продажу, что-бы получить все продажи ДО и ВКЛЮЧАЯ эту продажу, но не позже, тут по-моему алгоритм считает все продажи по продукту, а у меня задача посчитать все продажи по продукту на момент этой конкретной продажи, когда встретится следующая продажа то нарастающий итог подрастет. Т.е. для каждой продажу нужен именно нарастающий итог до и включая эту самую продажу, а не общая сумма по продукту. Или я неверно понял то, что Вы написали ?
surkenny, спасибо! пытаюсь разобраться как это работает - если яправильно понимаю внутрь функции группировки, вставляем List.Generate в функцию агрегации, я не столь искушенный юзер или кодер в данном случае, мне потребуется время для осознания )) сегодня или макс завтра вернусь с результатом ) еще раз благодарность за ответ. P.S. думаю тема ушла в сторону от фильтрации вложенных таблиц, это уже нарастающий итог по продукту с учетом даты или порядкового номера продажи) видимо я неверным путем изначально пошел и отсюда неверная тема.
написал: Константин Иванов, вот вместо Вашей рукописи лучше бы сделали то, что Msi2102 попросил. И отговорка, что Вы данные из БД тащите, не принимается Мы от Вас как раз данные и не хотим, а нужен пример этих данных. Конечно, если хотите решение получить, а не просто выговориться
Прошу прощения был в отъезде. PooHkrd, surkenny , Msi2102 Вот файл , еще раз постараюсь кратко, с этим у меня проблемы. Есть файл с продажами, каждая продажа содержит ID продукта, задача посчитать в Power Query нарастающий итог, на каждую продажу по этому конкретному продукту. P.S. Попробовал считать его сгруппировав продукты блоками т.е. отсортировав входную таблицу так что продажи кадого продукта идут строго подряд по возрастанию , затем по следующему продукту, и так далее. Применил List.Generate, на 150.000 продаж думаю будет обновление около часа - но генерально работает. P.P.S в DAX за счет связей между таблицами вобщем-то тоже легче посчитать, интересно решить эту задачу именно в Power Query в том числе что бы не нагружать отчет, одно дело он на минуту дольше будет обновляться зато потом работать быстро, другое дело он будет это считать и хранить в самом Power BI мне показалось это не оптимально, но может я ошибаюсь, читал тут мнение, что DAX лучше приспособлен к подсчетам и делает это быстрее, а Power Query скорее предназначен для подготовки сырых данных, а не для подсчетов.
Вобщем пока нарастающий итог удалось сделать несколькими способами, но все они помимо того, что убивают Query Folding весьма медленные, самое быстрое на 150.000 тысяч записей получилось в районе 12-15 минут, думал List.Generate или List.Accumulate будут быстрее, но они по всей видимости работают быстро в рамках подсчета нарастающего итога вообще, а когда 150.000 строк еще дополнительно имеют разбивку по ID продукта, то что мне удалось построить на их основе уходит по времени в бесконечность ((
Итак способы, которые я использовал
1. Прямая фильтрация исходной таблицы по индексу и ID продукта, это примрено на 4-5 часов обновления 2. Создание вложенных табличек по ID продукта и отдельная функция, которая каждую вложенную табличку в каждой строке фильтрует, что-бы индекс было до или равно текущей продажи и подсчитывает накопленную сумму это тоже на часы обновление.... 3. Заранее таблицу сортируем - что бы продажи сначала были отсортированы по ID продукта, как бы блоками получились внутри одной большой таблицы, затем внутри каждого продукта сортируем продажи по индексу , что бы они были в хронологическом порядке и затем добавив новый индекс уже в таком положении таблицы считаем накопительный итог для каждой продажи, 15 минут,пока это лучший результат. Вариантов с List.Accumulate или List.Generate пока построить не удалось, возможно там надо цикл в цикле делать - то есть общий цикл идет по табличке, а для каждой строки запускается собственный цикл который за счет индексов знает сколько строк надо проссумировать назад, что-бы получить нарастающий итог по текущему продукту
Спасибо за отклик ! я посмотрел КОД ночестно признаться решения своего вопроса тут не увидел, скачал , сейчас еще раз проанализирую но что-то пока не увидел тут решения своего вопроса ))
написал: Константин Иванов, для такой задачи фолдинг поломается в любом случае. Если надо чтобы быстро, то лучше скулем на стороне БД решать, вьюху сделать. А если все же надо через PQ, то какой объем строк?
Ну пока документов\строк немного, где-то 150.000 тысяч , те которые требуется проанализировать таким образом, но количество постоянно растет. Жаль, что сворачивание придется потерять, но если его терять, то какие варианты Вы бы считали наиболее оптимальными? в целом я могу это сделать отдельной табличкой и при момощи связей потом подцепить ее уже в Power BI к основной для анализа и построения отчетности. Наверное это не столь ужасно будет для производительности, поэтому за варианты с потерей Query Folding тоже буду благодарен! На стороне БД можно сделать, есть у нас девелопер, но я просто не хотел его дергать и мне самому эта задача представляется интересной, т.к. она в том или ином виде встречается в моей практике довольно часто и каждый раз приходится изобретать велосипед)) Мне вообще интересно именно решение с попыткой передать тем или иным способом внешний параметр внутрь функции - в даннном случае передать исходный Index в функцию, которая обрабатывает вложенную табличку. Видел решения через построение функции, но как добавить в нее изначальный Индекс не допёр ((( Так же есть решение через без передачи внешних данных внутрь функции, например List.Accumulate но мне кажется это медленное решение - тупо в изначалной табличке, опираясь на исходный индекс, перебирать по условию и ссумировать, если его же применять ко вложенным таблицам это вообще мне кажется очень долгим и неуклюжим решением, хотя я может чего-то не догоняю.
Не бейте сильно, если где-то уже данный вопрос обсуждался, но я курил и Chris Webb и все такое, прямого решения н нашел, хотя много полезных идей около моей проблемы прочитал. Итак есть таблица по сути три столбца Index - просто порядковый номер продажи в рамках системы, ID товара, Сумма продажи, необходимо на каждый товар, на каждую продажу подсчитать накопленную по нему сумму, по сути простой нарастающий итог, но посегментно, для каждого товара свой итог, а не общую сумму продаж. При этом желательно не использовать операции прерывающие Query Folding, по сути, ка кодин из вариантов решения, я сджойнил таблицу саму на себя и новом столбце получил вложенную таблицу с продажами по данному товару, но теперь вот незадача, надо отфильтровать вложенную таблицу, т.е. убрать все продажи с индексом (порядковым номером, Index из начала постановки задачи) больше чем у текущей строки, т.е. убрать все более поздние продажи, чем в текущей строке
Я пробовал Table.TransformColumns но она не принимает значение Index из внешнего окружения, моего понимания принципов работы PQ пока не хватает что-бы либо изобрести другое решение не прерывающее Query Folding либо довести это решение до ума, помогите плиз!