Как оптимально организовать хранение сущностей в базе данных, если они содержат зависящие от (естественного) языка поля?
Допустим, у нас интернациональный интернет-магазин.
Это значит, что товары должны иметь заголовки и описания на всех языках, на которые таргетирован магазин. Класс ниже (представлен в UML) не удовлетворяет данному условию, так как имеет поля title и description непонятно для какого языка.
Кстати, я добавил цену просто чтобы не спрашивали "что это за такой товар без цены?", но в отличие от строковых полей работа с ценами другая: надо при отправке данных на клиент пересчитать цену на местную валюту по текущему курсу, добавить налоги и/или пошлины, если необходимо. В случае же с заголовком и описанием данные останутся неизменными до тех пор, пока их не отредактируют.
Каким же образом организовать хранение данных так, чтобы и производительность была хорошая, и чтобы поддерживать сервис было легче?
Поскольку хороший вопрос предполагает собственные предварительные изыскания, то изложу те подходы, которые знаю.
Подход 1: Отдельная колонка для каждого языка
При отправке данных на клиент нужно будет сделать выборку данных в соответствии с желаемым языком. Хорошо так же после получения данных из БД преобразовать зависящие от языка поля в поля с едиными именем:
Достоинства
- Минимальные потери производительности
Недостатки
- Трудно ориентироваться в таблице (уже с 4-5 языками).
Подход 2: Разнесение данных по дочерним таблицам
Звучит как "отношения между таблицами", но похоже, что это не так. Во всяком случае, это не отношение "один к одному", потому что на одну таблицу "products" будет несколько таблиц локализаций.
Основная таблица будет такой же, что и в предыдущем варианте:
Однако теперь под каждый язык будет такая дополнительная таблица:
В итоге, перед отправке данных на клиент нужно будет заполнить поля первой таблицы данными из второй. Вероятно, для снижения потерь в производительности можно использовать дополнительные базы данных, хранящие данные в виде ключ-значение наподобие Redis или DynamoDB.
Ответы (2 шт):
Я бы посоветовал второй подход, но с одним словарём SringId/TextValue. StringId в продуктовой базе сделать с шагом 100, а в словаре добавлять к нему номер локализации.
После основного запроса языковой обработчик будет прибавлять локализацию ко всем StringId и извлекать текст из словаря.
Кстати, такой подход позволит значительно проще комбинировать части названий и описаний из составляющих и сэкономить объём БД.
Никаких "дополнительных полей" или "дополнительных таблиц" под каждый из языков не делают. Делают обычно примерно так.
В таблице
productsхранятся языконезависимые данные по каждому продукту, для каждого продукта используется уникальный ключ, назовём егоproduct_id. Кстати, почему у вас все поля ID описаны как строковые значения? Так никто не делает, основной ключ обычно целочисленное значение. Конкретно для продуктов строковое языконезависимое тоже обычно заводят, какой-нибудь SKU.В таблице
languagesхранятся записи о языках. У каждого языка есть уникальный идентификаторlanguage_id, остальное добавить по вкусу (тут уже смотря что вам надо - название, код локали и т.д.) Пример:language_id name code 1 English en-US 2 Русский ru-RU В таблице
descriptionsхранятся все локализации для каждого продукта. Выглядит это как-то так (иproduct_id, иlanguage_idв данном случае желательно сделать индексами):product_id language_id title description 1 1 First product title First product description 1 2 Название первого продукта Описание первого продукта 2 1 Second product title Second product description 2 2 Название второго продукта Описание второго продукта
Выборка данных по продукту осуществляется примерно таким запросом:
SELECT p.product_id, p.sku, ..., d.title, d.description FROM products p
JOIN descriptions d ON p.product_id = d.product_id
WHERE p.product_id = <pid> AND d.language_id = <lid>;



