Что происходит при правом join и попадёт ли пользователь без покупок в выборку в SQL
Что проверяют
Понимаете ли вы внешние соединения без подглядывания в справочник и не путаете ли стороны. Формулировка вопроса это ловушка: кандидату подсказывают ответ «попадёт» и смотрят, согласится ли он не думая.
Как отвечать
Ответ короткий: не попадёт. В запросе вида FROM Users RIGHT JOIN Purchases ON Users.Id = Purchases.UserId ведущей становится таблица покупок. В результат идут все её строки плюс подошедшие данные пользователя, а у пользователя без покупок строк в правой таблице нет, значит и в выборке его не будет.
Дальше покажите, что видите симметрию:
- останутся все строки правой таблицы, включая покупки, к которым не нашлось пользователя: у них поля пользователя будут
NULL; - чтобы увидеть пользователей без покупок, нужно левое соединение при той же расстановке таблиц, и пустыми окажутся уже поля покупки;
- правое соединение всегда переписывается в левое перестановкой таблиц, поэтому в проектах его почти не пишут: читать запрос со смешанными направлениями тяжело.
Под задачу «пользователи без покупок» сразу дайте запрос: левое соединение плюс условие Purchases.UserId IS NULL, либо NOT EXISTS с подзапросом, который на больших таблицах обычно выигрывает. Для дотнета уточните, что в LINQ правого соединения нет: его собирают из GroupJoin и DefaultIfEmpty, и провайдер переводит это в левое.
Где спотыкаются
Отвечают «попадёт, с NULL вместо покупки». Это поведение левого соединения, и такой ответ сразу показывает, что стороны перепутаны.
Второе: переносят условие из ON в WHERE. Фильтр по полям необязательной стороны отбрасывает строки с NULL и превращает внешнее соединение во внутреннее, а автор запроса продолжает считать его внешним и ищет пропавшие строки в данных.
Третье: считают покупки через COUNT со звёздочкой. У пользователя без покупок в левом соединении есть строка, поэтому получится единица вместо нуля. Считать надо по столбцу покупки, тогда пустые значения в счёт не попадут.