Вопросы по теме «JOIN и кардинальность»

Проверь себя · 1/3разбор после ответа
После JOIN таблиц users и events по user_id (связь один-ко-многим) вы хотите получить число пользователей, у которых был хотя бы один ивент. Какой расчёт даст корректное число?

Что произойдёт при JOIN, если в правой таблице несколько строк на один ключ? Сколько строк вернёт LEFT JOIN, если совпадений нет? На собеседовании дают две таблицы и просят предсказать результат без выполнения запроса. Понимание кардинальности при JOIN — навык, который отличает аналитика, способного писать корректные запросы.

Всего в этом разделе 20 вопросов. Каждый — с правильным ответом и кратким разбором теории. Разбито на 4 части по 5 вопросов.

Булева логика и фильтрыКачество данных и инвариантыВоронки и когортные рассужденияПостановка задачиДоли и процентыSanity-check и оценкаСегментация и конфаундингТеория множеств и дедупликацияВзвешенные средние и смешение

Вопросы 15 из 20

1В `events(user_id, event_id)` у пользователя может быть много строк, и в `orders(user_id, order_id)` тоже много строк. Вы соединяете их по `user_id`. Какая связь получается на ключе и как это влияет на строки?
AЭто `many-to-many`: строки по каждому `user_id` перемножаются, и возможен взрывной рост числа строк после соединения
BЭто `one-to-one`: по каждому `user_id` после соединения будет ровно одна строка, как при соединении уникальных ключей
CЭто `one-to-many`: общее число строк после соединения станет равно числу уникальных пользователей в обеих таблицах
DЭто `one-to-one`, если перед соединением применить `DISTINCT` к каждой из таблиц по полю `user_id`
Ответ: Два источника, где по ключу много строк, при `JOIN` дают `many-to-many` и риск взрывного роста числа строк.

Если у пользователя 5 событий и 2 заказа, после `JOIN` получится 10 строк только для него — это и есть размножение строк, которое ломает `COUNT(*)`, `SUM()` и даже `AVG()` при неверном уровне анализа. Чтобы избежать, выбирают правильный ключ соединения или делают предварительную агрегацию до нужной гранулярности. Варианты `one-to-one` и `one-to-many` неверно описывают ситуацию: при многих строках с обеих сторон связь по ключу всегда `many-to-many`. `DISTINCT` по `user_id` теряет данные, а не превращает соединение в `one-to-one`.

Подробный разбор →
2Вы сделали `LEFT JOIN` `users` → `events` по `user_id`, чтобы сохранить пользователей без событий. Как корректно посчитать количество пользователей на соединённой таблице, не попавшись в ловушку дублирования строк?
AИспользовать `COUNT(DISTINCT user_id)`, чтобы получить число уникальных пользователей и не дублировать их по числу совпавших событий.
BИспользовать `COUNT(*)`, считая, что после `LEFT JOIN` число строк автоматически равно числу пользователей в исходной таблице слева.
CИспользовать `COUNT(event_id)`, считая, что строки с `NULL` справа всё равно учтутся в счётчике пользователей без событий.
DИспользовать `SUM(event_id)`, считая, что сумма идентификаторов корректно отражает количество уникальных пользователей в результате.
Ответ: После `LEFT JOIN` к таблице с отношением «один-ко-многим» пользователи дублируются, поэтому для подсчёта пользователей нужен `COUNT(DISTINCT user_id)`.

`LEFT JOIN` сохраняет всех пользователей, но добавляет по строке на каждое совпавшее событие, создавая дубли. `COUNT(*)` в таком наборе ближе к числу событий, а `COUNT(event_id)` игнорирует строки без событий, где справа `NULL`. `COUNT(DISTINCT user_id)` возвращает число уникальных пользователей, включая тех, у кого нет ни одного события. Это и есть корректный ответ для метрики числа пользователей.

Подробный разбор →
3После `JOIN` таблиц `users` и `events` по `user_id` (связь один-ко-многим) вы хотите получить число пользователей, у которых был хотя бы один ивент. Какой расчёт даст корректное число?
ACOUNT(*) на результате JOIN с GROUP BY user_id для подсчёта уникальных пользователей с событиями
BCOUNT(user_id) после JOIN таблиц users и events с фильтром WHERE event_id IS NOT NULL
CCOUNT(DISTINCT event_id) на результате JOIN таблиц users и events для подсчёта уникальных пользователей
DCOUNT(DISTINCT user_id) на результате JOIN таблиц users и events по user_id для уникальных
Ответ: При связи один-ко-многим в `JOIN` для подсчёта пользователей нужен `COUNT(DISTINCT user_id)`, иначе вы посчитаете события.

Каждый пользователь с несколькими событиями даст несколько строк после `JOIN` — это размножение строк. `COUNT(*)` в таком датасете измеряет количество строк, а не количество пользователей. Чтобы считать пользователей, используйте `COUNT(DISTINCT user_id)` или заранее агрегируйте события до одного флага на `user_id`.

Подробный разбор →
4Вы хотите добавить к `orders` атрибут `category` из таблицы `products(product_id, category)` и не изменить количество строк заказов. Что важнее всего проверить про `products`, чтобы избежать дублирования?
AОтсутствие повторов `order_id` в заказах: кардинальность ключа `JOIN` в справочнике перед соединением можно не проверять
BУникальность `product_id` в справочнике: один к одному по ключу `JOIN`, и атрибутное соединение не создаёт дубликатов
CЗаполненность поля `category` на 100% во всех строках: при полном справочнике атрибутный `JOIN` не добавляет лишних строк
DИспользование `DISTINCT` после `JOIN`: повторы в справочнике не влияют на итоговое число строк заказов
Ответ: Если справочник один к одному по ключу `JOIN`, атрибутный `JOIN` не вызывает дублирования строк.

Проблемы начинаются, когда в справочнике на один `product_id` несколько строк — например, разные версии атрибутов. Тогда один заказ начинает соответствовать нескольким строкам и появляется дублирование. Проверка уникальности ключа — простой способ предсказать изменение числа строк ещё до расчёта метрик. Уникальность `order_id` в `orders` тут не поможет, заполненность `category` не имеет отношения к числу строк, а `DISTINCT` после `JOIN` маскирует дубли и легко искажает агрегаты.

Подробный разбор →
5В таблице `orders` 120 строк (по одной на `order_id`). В `order_items` ровно по 3 строки на каждый `order_id`. Сколько строк будет после соединения `orders` с `order_items` по `order_id` и почему?
AБудет 120 строк, потому что `JOIN` по `order_id` не должен менять число строк левой таблицы
BБудет 480 строк, потому что результат `JOIN` равен сумме строк двух таблиц: 120 плюс 360
CБудет 360 строк, потому что это соединение `один-ко-многим` и каждой строке `orders` соответствует 3 строки в `order_items`
DБудет 3 строки, потому что в каждом заказе ровно три позиции и `JOIN` сворачивает их в строки заказа
Ответ: При соотношении `один-ко-многим` после `JOIN` количество строк обычно становится равным числу строк в «многой» стороне.

Каждая строка из `orders` соединится с 3 строками из `order_items`, поэтому получится 120 * 3 = 360 строк. Это нормальное поведение для соотношения `один-ко-многим` и не является ошибкой само по себе. Ошибка появляется, если после такого `JOIN` считать метрики, которые должны быть на уровне заказа, без учёта дублирования строк.

Подробный разбор →
1234

Хотите тренировать интерактивно?

В приложении — таймер, прогресс, стрики и 1700+ вопросов по всем темам.

Открыть Карьерник в Telegram

Другие темы: Логика

Булева логика и фильтрыКачество данных и инвариантыВоронки и когортные рассужденияПостановка задачиДоли и процентыSanity-check и оценкаСегментация и конфаундингТеория множеств и дедупликацияВзвешенные средние и смешение