SELECT UserId, UserName, UserPhoto, ArticleId, ArticleText
FROM Users as u INNER JOIN Articles as a ON u.UserId = a.UserId
Структура ответа на этот запрос будет следующей:
UserId1 UserName1 UserPhoto1 ArticleId1 ArticleText1
UserId1 UserName1 UserPhoto1 ArticleId2 ArticleText2
Обратите внимание на дублирующиеся UserName1 и UserPhoto1. А ведь в одном из этих полей может храниться большой blob. А теперь представьте, что будет при большем количестве JOIN'ов!
Меня удивляет, что упоминания этого нюанса нет в каждой серьезной статье про JOIN большими красными буквами. Более того, я ни разу не встречал такого упоминания. Скажете, что это очевидно? Как сказать... Это неочевидно как минимум при использовании ORM (особенно для тех программистов, кто не работал с голым SQL).
У меня было подозрение, что это дублирование красиво разруливается на уровне провайдера базы данных таким образом, что уже считанные данные не отправляются повторно (что-то вроде кэша). Но если вспомнить о том, что с провайдером базы данных мы работаем в потоковом режиме, и он не хранит уже считанные данные, то этот вариант отпадает.
Этим вопросом я задавался несколько лет назад, и уже успел про него забыть, так толком и не разобравшись. Но недавно коллега поднял эту тему в одной из дискуссий, и чтобы удовлетворить свое любопытство, я задал вопрос на StackOverflow. Там приводится несколько решений, как можно избежать дублирования:
- Выполнение нескольких запросов в стиле lazy loading. Сначала считываем людей, а потом считываем статьи для пользователей со считанными идентификаторами. Мы избавляемся от JOIN и дубликации, но порождаем лишний запрос.
- Выполнение первого пункта в рамках хранимой процедуры с возвращением нескольких ResultSet'ов. Как и в первом варианте будет выполнено два запроса, но экономим один round trip.
- Реализовать навороченный запрос, который задает null для дублирующихся результатов выборки. Преимущество - один запрос, недостатки - более сложные запрос и логика обработки результатов выборки на клиенте
- Реализовать запрос, который агрегирует выборку для каждого пользователя в один кортеж (например, в формате CSV). Преимущества и недостатки аналогичны пункту 3.
Если вдруг дубликация при JOIN-выборках станет узким местом в производительности вашего приложения, теперь вы знаете, что делать :)
Комментариев нет:
Отправить комментарий