Структура базы данных для сети магазинов в разных городах

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

    Исходные требования к БД

    • В каждом городе своя база 1С с единой структурой, но разными товарами (70% общих, 30% уникальных).
    • У товаров три типа цен: розница, партнёрская, оптовая.
    • Склады распределены по городу, остатки хранятся отдельно.
    • На сайте город выбирается, и показываются товары из соответствующей базы 1С. Если города нет - по умолчанию Москва.
    • Количество товаров: 10 000-15 000 на город, в перспективе до 60 000+.

    Вариант 1: единые таблицы с полем города

    Создаётся общая таблица product, в которую добавляется столбец city (например, msk, spb). Аналогично для цен, складов и остатков. Выборка идёт с фильтром WHERE city = 'spb'.

    Плюсы

    • DRY - не нужно дублировать структуру таблиц для каждого города.
    • Простота добавления нового города - достаточно вставить записи с новым кодом.
    • Единые запросы для аналитики по всем филиалам.

    Минусы

    • Рост таблицы: 5 городов × 15 000 товаров = 75 000 строк. При 10 городах - 150 000+.
    • Скорость выборки падает, если не продуманы индексы. WHERE city='spb' без индекса будет сканировать всю таблицу.
    • Сложнее поддерживать уникальность артикулов - нужен составной ключ (city, product_id).

    Вариант 2: отдельные таблицы для каждого города

    Для каждого филиала создаются свои таблицы: spb_product, ekb_product и т.д. Структура одинаковая, но данные физически разделены.

    Плюсы

    • Максимальная скорость выборки: запрос идёт только по маленькой таблице (10-15 тыс. строк).
    • Простота бэкапов и переноса данных по одному городу.
    • Лёгкость в настройке прав доступа.

    Минусы

    • Нарушение DRY - при изменении структуры нужно менять все таблицы.
    • Сложность кросс-городских отчётов (нужен UNION или отдельный ETL).
    • Рост числа таблиц: при 20 городах - 80+ таблиц (4 сущности × 20).

    Рекомендация: компромиссный подход

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

    • Добавьте составной индекс (city, product_id) на таблицу product.
    • Для таблицы остатков - индекс (city, warehouse_id, product_id).
    • Используйте партиционирование по городу (например, в PostgreSQL - PARTITION BY LIST (city)). Это даст физическое разделение данных, как во втором варианте, но с единой логикой.
    • Для цен можно сделать отдельную таблицу product_price с полями city, product_id, price_type, value - это упростит добавление новых типов цен.

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

    Дополнительные советы по атрибутам и фильтрам

    Для фильтрации товаров (цвет, размер, бренд) используйте EAV-модель или JSONB-поле в PostgreSQL. Это избавит от создания десятков колонок. Индексируйте часто используемые атрибуты через GIN-индекс.

    Пример схемы для атрибутов:

    CREATE TABLE product_attribute (    product_id INT,    city VARCHAR(10),    attr_name VARCHAR(50),    attr_value TEXT,    PRIMARY KEY (product_id, city, attr_name));

    Для быстрого поиска по значению - добавьте индекс на (attr_name, attr_value).

    Вывод

    Единая таблица с партиционированием по городу - золотая середина между скоростью и удобством поддержки. Не бойтесь 60 000 строк: при правильных индексах и партициях запрос выполняется за миллисекунды. А при расширении сети вы просто добавляете новый партиция, не меняя архитектуру.

    Часто задаваемые вопросы