Таблица заказов электронной коммерции. Сохранить цены или использовать таблицу аудита / истории?

13

Я создаю свою первую схему электронной коммерции. Я читал вокруг предмета на некоторое время, и я немного запутался об отношениях между order_line_itemиproduct

А productможно было купить. В нем есть разные детали, но самое главное unit_price.

У order_line_itemнего есть внешний ключ к product_idкупленному, quantityприобретенному и тому unit_priceмоменту, когда покупатель приобрел продукт.

Большая часть того, что я прочитал, говорит о том, что объект unit_priceon order_line_itemдолжен быть явно добавлен (т.е. не указан через product_id). Имеет смысл, так как магазин может изменить цену в будущем, что испортит отчеты о заказах, отслеживание, целостность и т. Д.

Вещь, которую я не понимаю, - зачем напрямую сохранять unit_priceзначение в order_line_item?

Не лучше ли создать таблицу аудита / истории, которая документирует unit_priceизменение product?

Когда order_line_itemсоздается, product_auditдобавляется внешний ключ таблицы, и цена может быть получена (по ссылке) оттуда.

Мне кажется, есть много положительных сторон в использовании этого подхода (меньшее дублирование данных, история изменения цен и т. Д.), Так почему же он не используется чаще? Я не встречал пример схемы электронной коммерции, которая использует этот подход, я что-то упустил?

UDPATE: Похоже, мой вопрос касается медленно меняющегося измерения . Я все еще в замешательстве, поскольку медленно изменяющееся измерение относится к хранилищу данных и OLAP. Так можно ли применять типы медленного изменения размеров к моей основной базе данных процессов бизнес-транзакций (OLTP)? Мне интересно, если я смешиваю много понятий, был бы очень признателен за некоторые рекомендации.

Gaz_Edge
источник
Я бы на самом деле сделал и то и другое: сохраняю продажную цену в order_line и сохраняю историю цен на товары. Потому что оба служат разным целям. Хранение продажной цены сделает запросы по заказам намного проще и быстрее. С другой стороны, вы можете получить старые цены даже на товары, которые не были проданы.
a_horse_with_no_name
Безусловно, упрощение запросов не может быть единственной движущей силой сохранения окончательной цены каждый раз. В конце концов, это реляционная база данных - запросы не будут намного сложнее.
Gaz_Edge
3
Хранение продажной цены в строке заказа не является «нереляционным» (и я считаю, что оно нормализовано, так как продажные цены, по моему мнению, являются прямым атрибутом строки заказа). Искусство использования реляционной базы данных заключается в знании границ. Если вы управляете магазином со 100 товарами и 5 заказами в день, то хранение и получение старых цен не проблема. Если вы управляете рынком с миллионами продуктов, тысячами дилеров и сотнями заказов в минуту, тогда запрос этой истории будет проблемой.
a_horse_with_no_name
Где бы вы хранили историю цен? Из того, что я читаю, мне понадобятся две базы данных. Один для OLTP, один для OLAP. История уходит в OLAP?
Gaz_Edge

Ответы:

10

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

В дополнение к веб-транзакциям многие компании поддерживают продажи через другие каналы, например:

  • По телефону
  • Торговые агенты "в дороге"
  • В физическом месте (например, магазин, офис)

В этих случаях заказ может быть введен в систему через некоторое время после совершения транзакции. В этих обстоятельствах может быть трудно или невозможно правильно определить, какая историческая запись цены должна использоваться - хранение цены за единицу непосредственно в заказе является единственным возможным вариантом.

Несколько каналов часто создают другую проблему - разные цены на один и тот же продукт. Доплаты за телефонные заказы распространены - и некоторые клиенты могут договориться о скидке. Возможно, вы сможете представить все возможные цены для всех каналов в схеме вашего продукта, но включение этого в ваши таблицы заказов может стать (очень) сложным.

Везде, где разрешены переговоры, становится очень трудно связать историю цен с согласованной ценой заказа (если только у агентов нет очень узких пределов переговоров). Вам нужно хранить цену на самом заказе.

Даже если вы поддерживаете только веб-транзакции и имеете относительно простую структуру ценообразования, все еще есть интересная проблема, которую нужно преодолеть - как следует учитывать повышение цен в полетных транзакциях? Настаивает ли компания на том, что покупатель должен платить повышение или соблюдает первоначальную цену (когда товар был добавлен в корзину)? Если это последнее, то техническая реализация сложна - вам нужно найти способ убедиться, что вы поддерживаете ценовую версию в сеансе правильно.

Наконец, многие компании начинают использовать высокодинамичные цены. Для данного продукта может быть не одна фиксированная цена - она ​​всегда рассчитывается во время выполнения на основе таких факторов, как время суток, спрос на продукт и т. Д. В этих случаях цена не может быть сохранена против продукта в первую очередь!

Крис Саксон
источник
3

Я добавлю некоторые практические моменты, которые я видел.

  1. Продукты переходные.

    То, что они могут обозначать сегодня, может не совпадать с тем, что они использовали для обозначения год назад. Один и тот же код sku (и, следовательно, product_id) может ссылаться на разные варианты / виды продукта на разных этапах.

    Не все понимают все проблемы под рукой; следовательно, пользователь может изменить атрибуты исходного продукта вместо создания нового из своего собственного невежества. Много раз, это могло произойти из-за плана, на котором работает пользователь (эй! У меня может быть только 100 sku, так почему бы не продолжать менять старые, вместо того, чтобы обновлять план) Итак, вы видите, во многих тележках продукт никогда не будет означать одно и то же навсегда.

  2. Различные цены в зависимости от заказа и условий доставки

    Как отметил пользователь @Chris, в разных сценариях могут применяться разные цены.

    В большинстве тележек хранится не менее 3 различных полей - цена за единицу, сумма скидки и цена со скидкой. В более продвинутых вы найдете еще 2 - цена за единицу с налогом, цена со скидкой с налогом. Вы можете найти еще пару полей для описания стоимости доставки и дополнительных платежей. Налоговые проценты могут варьироваться в зависимости от штата, продукта, страны, способа доставки и т. Д., Как и другие виды затрат. Точно так же скидки могут варьироваться в зависимости от географии, рекламных акций, времени продажи и так далее. Следовательно, есть информация, которую можно получить только на уровне заказа, и эта объединенная информация не может быть сгенерирована из данных только в таблице продуктов.

  3. Разделение проблем

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

  4. Более простая масштабируемость для нескольких систем

    В большинстве случаев Система управления заказами, Механизм правил, Механизм каталогов, Система управления контентом создаются и обслуживаются как отдельные системы. Это помогает оптимизировать для различных условий нагрузки и генерировать специализированный интеллект для каждой системы. Таким образом, одна система не может быть выкуплена из-за отсутствия информации из другой системы.

  5. Более быстрое развитие и время выполнения

    Я использовал здесь термин «время разработки», хотя использование «времени отладки» было бы более подходящим. Всякий раз, когда происходит какая-либо новая разработка, будет быстрее, если необходимые данные будут доступны без добавления собственной сложности, потому что тогда будут сравнительно меньшие циклы отладки.

    Представьте, что вас попросили создавать отчеты по требованию для скидок, предлагаемых на ежедневной основе в течение определенного месяца полгода назад. Если у вас есть первоначальная цена, цена со скидкой в ​​1-2 таблицах вместе с заказом, подробностями заказа, это довольно просто. Однако, если вам нужно пойти и получить цены из другой таблицы, а затем применить скидки из другой таблицы, а затем выяснить детали, время разработки и выполнения будет выше.

Хороший дизайн должен пытаться оптимизировать столько же для будущего, сколько и для настоящего.

му 無
источник
2

Это может в конечном итоге стоить дороже при хранении, но я предпочитаю разместить все соответствующие детали продажи вместе с самой транзакцией, так что если по какой-либо причине наш контрольный журнал будет поврежден или администратор переопределит меры безопасности на месте, детали продажа, такая как: используемая валюта, цена за единицу, кол-во, применяемые налоги и стоимость, к которой они пришли и т. д. Я обычно храню это как XML, так что он может быть гибким от продажи до продажи.


РЕДАКТИРОВАТЬ: Чтобы расширить то, что я кратко сказал выше, в своем последующем комментарии ниже, и то, что @a_horse_with_no_name затронуло выше, избыточность данных транзакций не только важна, но и необходима в масштабе.

Я предполагаю, что вы строите с использованием ООП, и поэтому у вас, скорее всего, должен быть объект транзакции и либо всеобъемлющий объект продукта, и / или объект цены. По своему личному опыту, я предпочитаю быть многословным в своей истории, хранилище относительно дешевое.

Мы создали историю объектов, которую вы можете упростить, используя существующую RDBMS или некоторую разновидность хранилища значений ключей NOSQL (или, что еще лучше, RDBMS, которая позволяет NoSQL-подобным соединениям, таким как handlersocket или memcache), и мы сохраняем историю объектов Таким образом, каждая деталь и изменение цены в одном месте легко и быстро доступны. Если вы серьезно, вы можете даже использовать DIFF, чтобы сэкономить на хранилище и сохранить изменения только вперед, хотя у него есть свои предостережения. Это должно заботиться о вашей истории, и преимущество сериализованных объектов заключается в том, что ваша система сможет / сможет восстановить их как объекты, которые они хранили. Это заботится об истории.

Что касается моего предложения, хранение деталей транзакции, таких как налоги, валюта и т. Д., В самой транзакции означает, что вам не нужно искать другие детали в другом месте, ваш объект транзакции будет знать о его свойствах, и ваши представления могут позаботиться о представлении. переменные данные, как вы считаете нужным. Вы получаете быстрый доступ к снимку и получаете дополнительное преимущество избыточных и проверяемых записей.

Оно того стоит, поверь мне!

oucil
источник
это не имеет смысла говорить, что вы не используете его, потому что он может быть удален. Вы можете переопределить любые данные. Любая стратегия должна сохранять резервные копии
Gaz_Edge
@Gaz_Edge Они не являются взаимоисключающими, вы все равно можете использовать контрольный журнал и хранить детали с транзакциями, избыточность в этом случае оправдана. Никогда не оставляйте себя с данными этого важного предмета до единой точки отказа. В нашем случае я использую централизованное хранилище объектов в качестве механизма истории, а не пытаюсь построить историю для каждого типа объекта (например, транзакция или продукт в этом случае). У меня будет как весь объект транзакции в истории, так и все самые важные детали с самой записью. Это полностью в стороне от объектов продукта в истории.
oucil
А что, если я хочу запустить отчеты о заказах? Например, сколько товара х было куплено? Или сколько заказов превышает y? Сохранение в формате XML означает, что вы теряете возможность запрашивать данные, если я не ошибаюсь
Gaz_Edge
@Gaz_Edge Неверно, если вы говорите о MySQL, у вас есть SELECT ExtractValue(field_name, '/x/path/');возможность фильтровать такие вещи, как все транзакции в определенной валюте или все транзакции с определенной минимальной налоговой стоимостью или что-то еще. Отчеты большего масштаба могут быть сделаны из истории объекта. Для более масштабных отчетов вы можете настроить elasticsearchсервер / экземпляр, который имеет отчеты в стиле BigData, и он легко масштабируется во многие миллионы документов.
oucil
@Gaz_Edge Следует также упомянуть, что отчеты, о которых вы говорите (покупки по стоимости, продажи продукта и т. Д.), Являются общими отчетами, и в них должны быть значения, хранящиеся в виде столбцов с записью транзакции, для более быстрой обработки. Все, что важно, но не обязательно сообщается, часто может войти в XML. Данные моментальных снимков на самом деле предназначены только для двух вещей: 1. Проблема медленно меняющихся измерений и 2. Проверка и сравнение, когда клиент жалуется, и вам нужно быстро выяснить, кто прав. Это не для повседневного использования.
oucil
1

Мой голос - хранить цену за единицу для вашей позиции и отслеживать историю цен на ваши продукты в отдельной таблице. Я оправдываю это дополнительной гибкостью.

Даже если ваша структура ценообразования жесткая и четко определенная и не допускает изменений, упомянутых выше @Chris Saxon, вам удобно, что так будет всегда? Даже если вы уверены, зачем рисовать себя в углу? Я думаю, что было бы неплохо сохранить это в деталях позиции, потому что я не могу придумать вескую причину, чтобы отделить ее.

Что касается хранения вашей истории цен, есть определенная ценность в хранении этого отдельно, поскольку могут быть изменения в цене товара, и никто не купил его. Это определенно была бы полезная информация, чтобы знать, было ли изменение цены неэффективным. Как вы упомянули, это классический вариант использования медленно изменяющегося типа 2 в сценарии хранилища данных. Обычно любое изменение цены в вашей таблице продуктов будет зафиксировано, и в таблицу измерений будет добавлена ​​новая строка с обновленной ценой и отметкой времени, указывающей, когда произошло это изменение. В предыдущей строке будет обновлена ​​дата окончания, чтобы указать, что она больше не является эффективной ценой. Поэтому одним из подходов будет отслеживание изменений такого типа в хранилище данных.

Однако, если вы не хотите заниматься разработкой схемы хранилища данных и процесса ETL одновременно с разработкой базы данных электронной торговли OLTP, то эта история, безусловно, может быть сохранена в нашей базе данных электронной торговли. Это можно сделать, как вы описали, создав отдельную таблицу product_audit, которая свисает с таблицы продуктов и содержит даты начала и окончания, когда действовала эта версия продукта. Это также можно сделать в самой таблице продуктов, добавив даты начала и окончания в таблицу, чтобы указать, какой продукт в настоящее время активен. Однако, в зависимости от количества продуктов и количества или изменений в ценах, которые претерпевает ваша компания, это может сделать вашу таблицу продуктов намного больше, чем предполагалось, и впоследствии может вызвать проблемы с производительностью запросов.

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

njkroes
источник
Спасибо за ответ. Я думаю, что стратегия, с которой я иду, будет заключаться в разработке OLTP в соответствии с книгой. Мне действительно нравится идея иметь хранилище данных для отчетности. Хотя это добавляет дополнительную работу (создание новой схемы), схема может быть адаптирована к OLAP. Вроде того, чтобы избежать сценария, в котором я пытаюсь втиснуть квадратный колышек в круглое отверстие.
Gaz_Edge
@Gaz_Edge: Я нахожусь в подобной ситуации с точки зрения принятия решения для моего нового проекта. Не могли бы вы поделиться своими мыслями о том, какой подход к дизайну вы использовали год назад? Это сработало хорошо?
Кодер Абсолют
@CoderAbsolute Мое оригинальное решение было ориентировано на базу данных. Мое приложение теперь сосредоточено вокруг сервис-ориентированной архитектуры. Теперь у меня есть много небольших разрозненных схем баз данных, а не одна тесно связанная. Потребность в 3N для одной массивной схемы исчезла. Так что теперь я просто добавляю цену за единицу непосредственно к заказу. Сохранение исторических изменений цен на продукцию теперь исчезло, поскольку это не было бизнес-требованием. Надеюсь, это поможет.
Gaz_Edge
0

Я полностью согласен с основной идеей сохранения информации (контекста), относящейся к порядку. Небольшое замечание: такая ситуация возникнет только тогда, когда вы разрабатываете свое приложение, ориентированное на базу данных, и все вращается вокруг большой жирной базы данных. Если вы измените свою точку зрения, взглянув на проблемную область под другим углом, вы четко заметите, что порядок - это снимок очень особенного события в жизненном цикле вашего приложения. Когда вы решаете проблемы в зависимости от контекста, проблемы с базой данных становятся второстепенными, а сложности, которых все боятся запрашивать и создавать отчеты, будут беспрепятственно решаться в доменной модели.

Ваку-2
источник
Несколько небольших примеров сделают ваш ответ намного проще для понимания. Особенно такие предложения, как «заказ - это захваченный снимок особого события в жизненном цикле вашего приложения».
Дезсо