Комментарии 18
А если к этой схеме добавить покупателя, для каждого из которых прайс рассчитывается «на лету» на основании некоего набора базовых прайсов, то что и как будет в БД храниться?
0
Не очень понял, куда именно нужно добавлять покупателя. Для каждого покупателя будут свои прайсы? Если так, то просто добавляется в шапку покупатель, а затем в расчете priceListDetail (Product p, Stock s, DATE dt) добавляется еще один параметр, который затем используется в WHERE. И получаются разные цены для разных покупателей.
0
Да, для каждого покупателя свой прайс. Но я, видимо, взял более широкую задачу, когда прайс-лист не хранится в базе, а динамически рассчитывается в момент обращения на основании других прайслистов, описанная в статье схема не подходит для этих целей.
0
Можно сделать вот как: есть базовые прайсы как в статье. Дальше, на основе их уже высчитаны текущие цены: см. свойство price (Product, Stock, DATE). Дальше можно его умножать, или делить, например, на скидку покупателю (или любое другое выражение). То есть:
price (Customer c, Product p, Stock st, DATE d) = price(p, st, d) * (100 — discount( c )) / 100;
И дальше использовать эту цену в заказах или где-то еще.
price (Customer c, Product p, Stock st, DATE d) = price(p, st, d) * (100 — discount( c )) / 100;
И дальше использовать эту цену в заказах или где-то еще.
0
Интересно, какое будет быстродействие получения данных в таком случае, если, например, нужно по запросу отдать по API прайсы из 5000 позиций на 50 покупателей, если прайс будет вычисляться по сложной формуле, например, от объема продаж за текущий месяц с учётом возвратов и ещё парочки подобных критериев.
0
Хорошее быстродействие. Так как там никакого ORM не будет, а все скомпилируется в один (или чуть больше) оптимизированных SQL запросов, которые считают нужные данные.
Правда запросы будут зависеть от расставленных MATERIALIZED. Как я писал в статье: можно денормализовать через MATERIALIZED кортеж из PriceListDetail, Stock в одну таблицу и построить по ней индекс по полям (product, stock, fromDate). Тогда для каждого product и stock PostgreSQL будет находить цену по индексу за логарифм.
Если учитывать другие параметры (объемы продаж и прочее), то тоже можно сделать MATERIALIZED на них, и все будет хранится в таблицах покупателей (или других с небольшим количеством записей) и автоматически обновляться. Тогда к ним будет идти простой JOIN. Ну или не делать все эти MATERIALIZED, тогда СУБД будет их высчитывать в момент запроса (пойдут такие запросы).
У нас на практике в таких денормализованных таблицах хранятся сотни миллионов записей, и работает все достаточно быстро (хотя это относительно конечно). Но сделать быстрее в такой логике вряд ли получится.
Правда запросы будут зависеть от расставленных MATERIALIZED. Как я писал в статье: можно денормализовать через MATERIALIZED кортеж из PriceListDetail, Stock в одну таблицу и построить по ней индекс по полям (product, stock, fromDate). Тогда для каждого product и stock PostgreSQL будет находить цену по индексу за логарифм.
Если учитывать другие параметры (объемы продаж и прочее), то тоже можно сделать MATERIALIZED на них, и все будет хранится в таблицах покупателей (или других с небольшим количеством записей) и автоматически обновляться. Тогда к ним будет идти простой JOIN. Ну или не делать все эти MATERIALIZED, тогда СУБД будет их высчитывать в момент запроса (пойдут такие запросы).
У нас на практике в таких денормализованных таблицах хранятся сотни миллионов записей, и работает все достаточно быстро (хотя это относительно конечно). Но сделать быстрее в такой логике вряд ли получится.
0
Хранить каждый возможный критерий в таблице покупателей — так себе идея, мне кажется. Тогда при добавлении новых критериев будет реструктуризация таблиц и пересчет по всем покупателям. Или нет?
0
Не обязательно каждый возможный. На lsFusion сначала делается логика, а потом уже в эксплуатации добавляются MATERIALIZED на промежуточные свойства, чтобы ускорить чтение, но замедлить чуть чуть запись. Более подробно мы писали вот в этой статье.
Когда добавляется новый MATERIALIZED на старте автоматически создается поле и рассчитывается по нужной формуле. Обычно это идет достаточно быстро, так как этот расчет идет не на сервере приложений, а одним запросом на SQL сервере и делается достаточно быстро (мы на практике добавляем поля как в таблицы с сотнями миллионов записей, так и где расчет зависит от таких таблиц). И это все занимало максимум один час. Добавление пустой колонки в PostgreSQL вообще мгновенная операция.
В любом случае, тут 2 варианта: либо считать в момент запроса, либо при записи инкрементно обновлять значения. Третьего не дано. И в lsFusion это все делается прозрачно, без изменения остальной логики приложения.
Когда добавляется новый MATERIALIZED на старте автоматически создается поле и рассчитывается по нужной формуле. Обычно это идет достаточно быстро, так как этот расчет идет не на сервере приложений, а одним запросом на SQL сервере и делается достаточно быстро (мы на практике добавляем поля как в таблицы с сотнями миллионов записей, так и где расчет зависит от таких таблиц). И это все занимало максимум один час. Добавление пустой колонки в PostgreSQL вообще мгновенная операция.
В любом случае, тут 2 варианта: либо считать в момент запроса, либо при записи инкрементно обновлять значения. Третьего не дано. И в lsFusion это все делается прозрачно, без изменения остальной логики приложения.
0
Понятно. Ещё вопрос не совсем по теме — у вас есть какие-нибудь готовые наработки по реализации геотрекинга и работе с картографической информацией на вашей платформе?
0
К сожалению, пока ничего нет в это части. Мы пока акцентируем внимание на бизнес-приложениях и информационных системах, но для таких вещей есть из коробки JSON API, возможность прямо из lsFusion кода делать HTTP вызовы, и возможность спускаться на уровень ниже и писать на Java.
0
Критерий в таблице покупателей хранить не надо. Можно сегментировать клиентов по этим критериям (и пересегментировать при необходимости). И в расчёте цены применять ценочувствительные сегменты клиента, а не его свойства.
0
Можно и так, конечно, но в предельном случае каждый сегмент будет состоять из одного клиента :)
0
Кстати, да — можно через сегменты. В lsFusion это делается легко, вот так:
soldSum по покупателю — это по какой-то формуле рассчитанный объем продаж. При этом сегменты в такой реализации будут хранится в таблице покупателей и автоматически пересчитываться при изменении суммы продаж.
CLASS Segment 'Сегмент';
name 'Наименование' = DATA ISTRING[50] (Segment);
threshold 'Порог' = DATA NUMERIC[14,2] (Segment);
discount 'Скидка' = DATA NUMERIC[8,2] (Segment);
segment (Customer c) =
GROUP LAST Segment s
ORDER threshold(s), s
WHERE soldSum(c) >= threshold(s) MATERIALIZED;
price (Customer c, Product p, Stock st, DATE d) =
price(p, st, d) * (100 (-) discount(segment(c))) / 100;
soldSum по покупателю — это по какой-то формуле рассчитанный объем продаж. При этом сегменты в такой реализации будут хранится в таблице покупателей и автоматически пересчитываться при изменении суммы продаж.
+1
Всё-таки, при таком подходе мне пока видится, что для более-менее сложных систем будет очень большая зависимость от архитектуры, заложенной при проектировании, в некоторой мере в ущерб гибкости. Поэтому было бы интересно увидеть статью именно о проектировании архитектуры достаточно сложной системы, с подробным обоснованием выбранных решений.
0
Как раз наоборот. Наша практика показала, что это очень гибкая схема при необходимости изменений. Например, свойство price для 4х ключей объявляется как-то, затем используется в тысячи мест. А когда нужно изменить расчет, то он просто меняется, а все остальное продолжает работать как и раньше. При этом на нативном уровнем, все продолжает компилироваться в SQL.
Собственно мы и пишем статьи о том, какие «шаблоны» мы используем при проектировании сложной системы. Она и состоит из множества таких архитектурных решений. В статье есть ссылка на сложную систему — ERP-систему для розницы.
Поэтому было бы интересно увидеть статью именно о проектировании архитектуры достаточно сложной системы, с подробным обоснованием выбранных решений.
Собственно мы и пишем статьи о том, какие «шаблоны» мы используем при проектировании сложной системы. Она и состоит из множества таких архитектурных решений. В статье есть ссылка на сложную систему — ERP-систему для розницы.
0
Если есть уже готовое описание архитектуры и функциональная модель этой сложной ERP-системы для розницы, с интересом бы ознакомился.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Управление параметрами в бизнес-приложениях по аналогии с системой контроля версий