Базы Данных (Data Base) | SQL & NoSQL. PostgreSQL, MySQL и Big Data. Уроки для Backend и Data Engineer. Архитектура БД, Оптимизация и Design.
IT и технологии · 6 июня 2026 г.
🛑 Антипаттерн: "Списки через запятую" в базе данных — 6 июня 2026 г. в 06:34:15.823
🛑 Антипаттерн: "Списки через запятую" в базе данных Признайтесь, у каждого был соблазн сделать это. У вас есть сущность (например, User), и нужно сохранить список их ролей или IDs купленных товаров. Создавать отдельную таблицу кажется оверхедом, и вы решаете: "А, запишу просто строкой через запятую". В БД это выглядит так: role_ids: "1,4,12" Почему это бомба замедленного действия и как это лечить? Давайте разбираться. Почему это плохо: 1. Сложный поиск. Найти всех пользователей с role_id = 1 через LIKE '%1%' - это больно. Вы найдете и 1, и 12, и 100. Придется писать монструозные регулярки. 2. Никаких индексов. База данных не может эффективно индексировать подстроки в таком формате. Full scan обеспечен. 3. Проблемы с JOIN. Вы не сможете сделать нормальный JOIN с таблицей ролей. 4. Целостность данных. Вы не можете повесить Foreign Key. Никто не помешает записать туда "1, 4, apple, NULL". 5. Атомарность обновлений. Удалить роль 4 из строки "1,4,12" - это чтение, парсинг на бекенде и перезапись. Состояние гонки (race condition) гарантировано. Как делать правильно: Вариант 1: Классическая нормализация (Junction Table) Создайте связующую таблицу. Это золотой стандарт для реляционных БД (PostgreSQL, MySQL, Oracle). -- Плохо ❌ CREATE TABLE users ( id INT PRIMARY KEY, name VARCHAR(50), role_ids VARCHAR(255) -- "1,2" ); -- Хорошо ✅ CREATE TABLE user_roles ( user_id INT, role_id INT, PRIMARY KEY (user_id, role_id), FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (role_id) REFERENCES roles(id) ); Теперь выборка всех админов - это моментальный запрос с использованием индексов. Вариант 2: Массивы или JSONB (PostgreSQL) Если вы используете PostgreSQL и вам действительно не нужны жесткие FK (Foreign Keys) на каждый элемент, можно использовать нативные типы. -- Допустимо в Postgres ✅ CREATE TABLE users ( id SERIAL PRIMARY KEY, name TEXT, role_ids INT[] -- массив целых чисел ); -- Поиск (очень быстрый с GIN индексом): SELECT * FROM users WHERE 1 = ANY(role_ids); Никогда не храните списки в VARCHAR, если вам когда-либо придется искать по содержимому этого списка или джойнить его. Экономия 5 минут на старте обернется часами рефакторинга позже. #db 👉 @database_info

