Структура базы данных для сети магазинов в разных городах
При разработке интернет-магазина, который работает с несколькими городами и отдельными базами 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 строк: при правильных индексах и партициях запрос выполняется за миллисекунды. А при расширении сети вы просто добавляете новый партиция, не меняя архитектуру.