Как оптимально организовать хранение сущностей в базе данных, если они содержат зависящие от (естественного) языка поля?

Рейтинг: 0Ответов: 2Опубликовано: 03.08.2025

Допустим, у нас интернациональный интернет-магазин. Это значит, что товары должны иметь заголовки и описания на всех языках, на которые таргетирован магазин. Класс ниже (представлен в UML) не удовлетворяет данному условию, так как имеет поля title и description непонятно для какого языка.

введите сюда описание изображения

Кстати, я добавил цену просто чтобы не спрашивали "что это за такой товар без цены?", но в отличие от строковых полей работа с ценами другая: надо при отправке данных на клиент пересчитать цену на местную валюту по текущему курсу, добавить налоги и/или пошлины, если необходимо. В случае же с заголовком и описанием данные останутся неизменными до тех пор, пока их не отредактируют.

Каким же образом организовать хранение данных так, чтобы и производительность была хорошая, и чтобы поддерживать сервис было легче?

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

Подход 1: Отдельная колонка для каждого языка

введите сюда описание изображения

При отправке данных на клиент нужно будет сделать выборку данных в соответствии с желаемым языком. Хорошо так же после получения данных из БД преобразовать зависящие от языка поля в поля с едиными именем:

введите сюда описание изображения

Достоинства

  1. Минимальные потери производительности

Недостатки

  1. Трудно ориентироваться в таблице (уже с 4-5 языками).

Подход 2: Разнесение данных по дочерним таблицам

Звучит как "отношения между таблицами", но похоже, что это не так. Во всяком случае, это не отношение "один к одному", потому что на одну таблицу "products" будет несколько таблиц локализаций.

Основная таблица будет такой же, что и в предыдущем варианте:

введите сюда описание изображения

Однако теперь под каждый язык будет такая дополнительная таблица:

введите сюда описание изображения

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

Ответы

▲ 3

Никаких "дополнительных полей" или "дополнительных таблиц" под каждый из языков не делают. Делают обычно примерно так.

  • В таблице 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>;
▲ 0

Я бы посоветовал второй подход, но с одним словарём SringId/TextValue. StringId в продуктовой базе сделать с шагом 100, а в словаре добавлять к нему номер локализации.

После основного запроса языковой обработчик будет прибавлять локализацию ко всем StringId и извлекать текст из словаря.

Кстати, такой подход позволит значительно проще комбинировать части названий и описаний из составляющих и сэкономить объём БД.