Дизайн базы данных: как моделировать общие ценовые факторы продукта/услуги?
Я пытаюсь создать общую модель данных, которая позволит определенному продукту (указанному FK product_id в приведенной ниже таблице ниже) указать 0 или более факторов цены (я определяю "коэффициент" как единицу от цены, добавленной или вычитаемой, чтобы получить общее количество).
Так скажите, что есть эта таблица:
===============================
price
===============================
price_id (PK)
product_id (FK)
label
operation (ENUM: add, subtract)
type (ENUM: amount, percentage)
value
Книжная цена может быть представлена следующим образом:
====================================================================
price_id | product_id | label | operation | type | value
====================================================================
1 | 10 | Price | add | amount | 20
2 | 10 | Discount | subtract | percentage | .25
3 | 10 | Sales Tax | add | percentage | .1
Это в основном означает:
Price: $20.00
Discount: - $5.00 (25%)
--------------------
Sub Total: $15.00
Sales Tax: $1.50 (10%)
------------------------
Total: $16.50
Несколько вопросов:
- Что-то явно не так с первоначальным дизайном?
- Что делать, если я хотел создать "шаблоны" (например, шаблон "общий товар", который имеет поля "цена", "скидка" и "налог с продаж" ), "роскошный товар", который имеет "цену", "скидку", поля "налог на роскошь" ) - как бы я смоделировал это?
- Вышеупомянутая модель работает, если каждая запись относится к общей сумме предшествующей записи. Таким образом, в примере "налог с продаж" применяется к разнице "цена" и "скидка". Что, если бы сумма не была рассчитана так просто? Например: A + B + (A + 10%) - (B - 5%). Как бы я смоделировал это?
- Кроме того, что, если тип "процент" не применяется к непосредственно предшествующей строке (как это подразумевается под вопросом № 3) и применяется к более чем одной строке? Нужна ли мне другая таблица, чтобы указать, какая цена- > цена_ид, к которой относится процент?
Ответы
Ответ 1
Прежде всего вам нужна модель ценовых меток, которая проста:
price_labels
id | label
1 | Price
2 | Discount
3 | Tax
Затем немного измененная версия таблицы образцов, которую вы указали:
products_prices
price_id|product_id|label_id|divider|value
1 10 1 1 20
2 10 2 100 -25
3 10 3 100 10
Здесь я просто заменил метку соответствующим id из таблицы price_labels в качестве внешнего ключа. Кроме того, я опустил поле типа, которое тривиально, так как значение может быть положительным или отрицательным числом с плавающей запятой. Я добавил столбец разделителя, чтобы включить параметр процента. Я думаю, что это легче читать так же, потому что вы говорите (и думаете) "минус двадцать пять процентов" не 0,25.
Теперь выражение "абстракция" часть немного сложнее и может быть много решений.
price_expressions
product_id | date_from | date_until | expression
10 |2011-11-02 04:00:00 |2011-11-12 04:00:00 | (SELECT divider*value from
products_prices
WHERE product_id=%PRODUCT_ID%
AND label_id=1)*
(SELECT 1+value/divider from products_prices
where product_id=%PRODUCT_ID% AND
label_id=2)*
(SELECT 1+value/divider from products_prices
where product_id=%PRODUCT_ID% AND
label_id=3)
В поле выражения вы можете сохранить сложный оператор SQL, в котором вы можете просто заменить% PRODUCT_ID% placeholder значением product_id из той же строки:
SELECT REPLACE(expression,'%PRODUCT_ID%',CAST(product_id AS char))
AS price_expression FROM price_expressions
WHERE product_id = 10 AND date_from>=DATE_OF_PURCHASE
AND date_until<=DATE_OF_PURCHASE
Есть два возможных варианта этого способа, которым я его вижу:
- Вы можете изменить условие product_id =% PRODUCT_ID% и label_id = N только с ценой_id = N, поскольку вы уже сохранили его в таблице products_prices
- Вы можете использовать другой формат выражения, например. % PRICE_ID_1% *% PRICE_ID_2 и выполнять подстановки и вычисления на уровне приложения не напрямую в SQL
Надеюсь, что это поможет.
Ответ 2
Это кажется немного переработанным.
1) Не был бы ли процент налога на продажу фактором, в котором был куплен товар, а не какой товар был приобретен? Я мог видеть поле для "IsTaxable", но указание скорости для каждого элемента кажется неправильным.
2) Вы уверены, что вам нужно взять на себя расходы на создание этого родового? Вы уже достаточно уверены, что в будущем будет больше факторов? Если нет, не переуплодируйте его.
Предлагаемый дизайн:
- Добавить столбцы в таблицу продуктов для IsTaxable, DiscountPct и Unit Price.
- Сохранять процент налога с продаж в другой таблице. Возможно, таблица счетов.
Ответ 3
Относительно вашего вопроса 1:
Существует потенциальная функциональная зависимость между label
, operation
и type
. Например, скидка всегда может означать вычитание и процент. Если это так, модель данных может быть нормализована путем перемещения этих полей в отдельную таблицу с label
как PK.
Кстати, де-нормированная модель данных может быть законным инструментом для повышения производительности и/или простоты.
Относительно вашего вопроса 2:
Вот модель, которая позволяет легко "шаблонизировать":
![enter image description here]()
Окончательная цена продукта рассчитывается путем применения серии шагов по цене, в порядке, определенном STEP_NO. Несколько продуктов могут легко использовать один и тот же "шаблон" (т.е. Тот же PRICE_ADJUSTMENT_ID).
Относительно ваших вопросов 3 и 4:
Вам нужно смоделировать полное дерево выражений, а не просто "линейную" последовательность шагов. Существует несколько способов сделать это, большинство из них довольно сложны в реляционной парадигме. Возможно, самым простым является сохранение модели данных, аналогичной приведенной выше, но относиться к ней как к Reverse Polish Notation.
Например...
A + B + (A + 10%) - (B - 5%)
... может быть представлена как:
OPERATION TYPE VALUE
---- ---- -----
value A
value B
add
value A
percentage 10
add
add
value B
percentage 5
subtract
subtract
Вы уверены, что вам действительно нужна такая функциональность?
Ответ 4
Если некоторые ценовые факторы зависят от типа элемента, тогда у вас будет набор факторов цены, связанных с объектами в таблице ItemType, а ItemType будет свойством объекта item (внешний ключ, ссылающийся на ItemType), Если другие ценовые факторы связаны с языковым стандартом, в котором этот предмет продается или отправляется (например, налог с продаж), то эти факторы будут связаны с Locale и будут вызываться на основе адреса клиента. Обычно вы применяете факторы типа позиции на уровне позиций и факторы, зависящие от локали, к общей сумме счета. Sin-tax будет привязан к Dyad ItemTypeLocale и применяется на уровне позиции.
Ответ 5
1/Мне кажется, вам также необходимо рассмотреть последовательность
например. Цена - скидка + налог с продаж, очевидно, приемлема, но цена + налог с продаж - скидка не такова и цена - (скидка + налог с продаж)
2/Я подумал бы о цене в другой таблице. Разве это не деталь продаваемого предмета? Например. Виджет, синий, 20,00 $. В то время как ваши факторы являются деталями типа продаж. Предположительно, у вас может быть один набор факторов для прогулочной розничной продажи, другой для онлайн-продажи и третий для оптовой продажи. Вы можете рассчитать фактическую цену для этих трех типов продаж из базовых цен * факторов.
3/Я думаю, вам нужно больше таблиц; например возможно, Item, Sale type и factor_details и factor_rules. Возможно, ваш тип продажи покрыт вашим примером предметов роскоши, в этом случае (если элемент является только одним видом продажи), это может быть в таблице товаров. Factor_rules будет подробно описывать формулу расчета и factor_details значения.
Я считаю это довольно интересным. Я был бы признателен, если бы вы обновили этот вопрос своими впечатлениями, как только вы это проработали.