Это не магия. Это математика множеств
Каждый раз, когда алгоритм TikTok подбирает тебе следующий ролик, Spotify выдаёт «идеальный» плейлист, а маркетплейс показывает «ты смотрел похожее» — за кулисами работает база данных. И она задаёт вопросы на SQL.
Не «тыкает пальцем в небо». Именно запрашивает данные из миллионов строк, сравнивает, пересекает, объединяет — и выдаёт тебе тот самый ролик, от которого ты уже час не можешь оторваться.
Сегодня разберём два инструмента, которые лежат в основе этого: операторы множеств и JOIN. И заодно поймём, почему SQL — это не про «таблички в Excel», а про управление реальностью данных.
Данные — это множества. Буквально
Вот инсайт, который меняет отношение к SQL: каждая таблица в базе данных — это математическое множество. Строки — элементы. Запросы — операции над множествами.
Это не метафора. Это буквально то, на чём построена реляционная модель.
И как только ты это понимаешь, три оператора — UNION, EXCEPT, INTERSECT — перестают быть «непонятными словами из документации» и становятся логикой, которую ты уже знаешь из школьной математики.
UNION — объединение. Берёшь два списка, склеиваешь их в один.
INTERSECT — пересечение. Оставляешь только то, что есть в обоих.
EXCEPT — разность. Берёшь первый список и вычитаешь из него второй.
Просто? Да. Но посмотри, что из этого вырастает на практике.
Как Spotify находит треки, которые ты ещё не слышал
Представь: у Spotify есть таблица с треками, которые ты слушал, и таблица с треками, которые слушают люди с похожим вкусом.
-- Треки "похожих" слушателей, которых ещё нет в твоей истории
SELECT track_id FROM similar_users_history
EXCEPT
SELECT track_id FROM your_history;
Одна строчка EXCEPT — и готов кандидат для рекомендации. Ничего лишнего.
А теперь представь обратную задачу: найти треки, которые вы оба слушали — чтобы подтвердить, что вкусы действительно совпадают.
SELECT track_id FROM your_history
INTERSECT
SELECT track_id FROM similar_users_history;
Это и есть коэффициент Жаккара — метрика схожести, которую используют в рекомендательных системах, анализе текстов и кластеризации пользователей. SQL считает его через обычные операторы множеств.
За каждым «вам может понравиться» стоит именно такая математика.
JOIN: как данные из разных таблиц становятся одним ответом
Теперь про JOIN — и это уже уровень «как устроен весь цифровой мир».
В реальных базах данных данные намеренно разложены по разным таблицам. Информация о пользователе — в одной. Его заказы — в другой. Товары — в третьей. Это называется нормализацией, и это не баг, а фича: так данные не дублируются и не противоречат друг другу.
Но когда нужен ответ — например, «какие товары заказал конкретный пользователь» — таблицы нужно соединить. Для этого и существует JOIN.
-- Кто и что заказал — данные из трёх таблиц в одном запросе
SELECT
users.name,
products.title,
orders.created_at
FROM orders
JOIN users ON orders.user_id = users.id
JOIN products ON orders.product_id = products.id;
Три таблицы, два JOIN, одна строчка результата для каждого заказа — и ты видишь полную картину. Именно так работает аналитика в любом стартапе: берёшь сырые данные из разных источников и «склеиваешь» их в осмысленный отчёт.
Декартово произведение: когда SQL делает всё возможное
Есть один момент, который ломает шаблон. Что будет, если соединить две таблицы без условия?
SELECT * FROM users, products;
База данных вернёт каждую строку пользователей с каждой строкой товаров. Если 1000 пользователей и 500 товаров — получишь 500 000 строк. Это декартово произведение, и в большинстве случаев это катастрофа производительности.
Но именно из этого «всего возможного» JOIN с условием и вырезает нужное. По сути, JOIN — это декартово произведение плюс фильтр.
Понимаешь, как работает механизм — понимаешь, почему забытый ON в JOIN может положить запрос на миллионную таблицу.
NOT EXISTS vs NOT IN: тихая разница с громкими последствиями
Последний момент — маленький, но важный. Особенно если ты когда-нибудь будешь работать с реальными данными, где бывают пропущенные значения.
Задача: найти пользователей, которые ни разу не делали заказ.
Кажется, всё просто:
SELECT name FROM users
WHERE id NOT IN (SELECT user_id FROM orders);
Но если в колонке user_id хотя бы одна строка содержит NULL — этот запрос вернёт пустой результат. Полностью пустой. Без ошибки, без предупреждения.
Правильный способ:
SELECT name FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.id
);
NOT EXISTS корректно обрабатывает NULL — и ведёт себя предсказуемо.
Это один из тех подводных камней, на которых горят даже опытные разработчики. Знаешь про него заранее — уже в выигрыше.
Итог: SQL — это не про таблички
SQL — это язык, на котором ты говоришь с данными. Он стоит за рекомендациями в стримингах, матчмейкингом в играх, антифродом в банках, лентой новостей в соцсетях и аналитикой в любом продукте, который ты используешь каждый день.
UNION, EXCEPT, INTERSECT — это теория множеств, применённая к реальным данным.
JOIN — это способ собрать разрозненные кусочки информации в единую картину.
И когда ты понимаешь, как это работает изнутри — ты перестаёшь быть просто пользователем цифровых сервисов и начинаешь понимать, как они устроены.
💡 Хочешь копнуть глубже? Полный учебный материал с детальными примерами, схемами и крутыми иллюстрациями ждёт тебя на нашем сайте!