Как правильно извлечь связанные данные без ORM?

Не могу понять, как правильно извлечь связанные данные из базы данных. Например, у меня есть две таблицы, в первой таблице хранится информация по авторам [Authors] (имя, фамилия, дата рождения и смерти, и тд), во второй таблице хранится информация по книгам [Books] (название, жанр, дата и тд). Книги сылаются на авторов (Books.Author -> Authors.Id).

Authors [ Id | Firstname | Lastname | Birthday | Death | ... ]
Books [ Id | Author | Title | Genre | Date | ... ]

У каждого автора множество книг. Я бы хотел извлечь эти данные в класс, который содержит все данные по автору и список всех его книг. Как правильно извлечь эти связанные данные не используя ORM?

Я вижу два варианта:

1) Сначала извлечь все данные из таблицы авторов (select * from Authors), а затем из таблица книг (select * from Books). Далее привести эти данные в нужный вид. Данный способ меня настараживает тем, что данные по авторам и книгам извлекаются отдельными запросами, нет целостности/атомарности что-ли.

2) Извлечь сразу все связанные данные (select * from Authors A left join Books B on B.Author = A.Id), а потом преобразовать в массив нужного класса. Данный способ не нравится тем, что полные данные автора будут у каждой книги. Будет большое количество столбцов.

Как лучше сделать? Может другие способы?

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному1
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Абсолютно адекватный вопрос, а комменты местами, как говорится, доставляют.

1.

Данный способ меня настараживает тем, что данные по авторам и книгам извлекаются отдельными запросами, нет целостности/атомарности что-ли.

Это правильная настороженность, называется Read Skew. Если нельзя пренебречь, решается транзакцией уровня Repeatable Read, что на современных MVCC БД недорого.

2.

Данный способ не нравится тем, что полные данные автора будут у каждой книги. Будет большое количество столбцов.

Это тоже правильное сомнение, может получится слишком большой Cartesian Product, который надо передавать с БД в приложение.

Как лучше сделать?

Можно и так, и так, в зависимости от исходных данных какой-то способ может оказаться лучше. Ну, т.е., если у автора «тяжелые» данные, второй способ начнёт проигрывать.

Может другие способы?

Есть, называется MultiSet.

Давайте, чтобы было более наглядно, к автору добавим ещё коллекцию... не знаю... любимых песен. Тогда во втором способе Cartesian Product будет Authors * Books * Songs. Вручную либо с помощью ORM его конечно, разобрать можно, но увеличение, заметим, кратное.

Способ, если БД такое поддерживает, заключается в том, чтобы сделать группировку сразу в БД и вернуть JSON. Из ОRM-ов такое умеют делать, например, BlazeDS или JOOQ.

Опять же, в зависимости от данных, первый способ (особенно, если ORM типа Hibernate имеет кеш) может оказаться проще и быстрее.

транзакцией уровня Repeatable Read, что на современных MVCC БД недорого.

Например, у MySql InnoDB такой уровень по дефолту.

Оба варианта подходят, в зависимости от ситуации. Именно так делают ORM. Например, EF Core 2 делает по первому варианту, а EF Core 3 — уже по второму. В любом случае, если речь идёт именно о SQL-базе, то без «ручной» последующей обработки данных в коде не обойтись.
В первом варианте придётся в коде джойнить две коллекции. Во втором — устранять избыточность по авторам, так как тут ты фактически денормализируешь данные.

Це тільки я помітив кореляцію між кількістю вайтишників та кількістю безглуздих технічних питань на сироварні?

Відповіді не менш безглузді

Вообще удивляюсь местной публике. Когда приходит «33 года всю жизнь мечтал подскажите с чего начать», так все сюсюкаются и список книг дадут и приободрят. А когда приходит человек с реальным техническим вопросом и примерами кода — обольют помоями

Извлечь можно как одним запросом, так и двумя. В идеале — померять что быстрее на достаточно большой базе. Но многие не меряют а тупо берут второй способ потому что «я ж крутой, я джойны знаю».

Про целостность при первом варианте — нужно понимать что будет страшного если одна из таблиц успеет обновиться между запросами. А не будет ничего страшного.

в спринге пишешь неймед квери и интерфейс-проекцию и вуаля!

Во первых нужно понимать контекст — для чего эти данные?
Если для страницы аля каталог — где идет листинг авторов, и рядом у каждого автора список книг, то в реальном кейсе, на ревью, тебе скорее всего предложат сделать декомпозицию, ибо 1 функционал который вытягивает и авторов и все их книги — избыточен и не переиспользуем.

Поэтому у тебя будет 2 класса ( мыслим ООПшно) — датапровайдера, 1 -ответственный за возвращение авторов, второй — книг.

AuthorsDataProvider::getAuthors(int $page, int $limit, $criteria = [] );
BooksDataProvider::getBooksByAuthorsIds(array $authorIds, int $page, int $limit, array $criteria = []);

Это все довольно схематично, но подход понятен. И да, как видно из примера, используй пагинацию — никто не вытаскивает все данные из бд для грида без пагинации.

Массивчиком также можно передавать поля по которым сортировать можно результаты — добавь такой функционал в свой проект, будет полезно.

Заодно напишешь свой велосипед пагинации, поймешь как работать с offset / limit, высчитаешь их исходя из номера страницы и кол-ва строк на страницу ( элементарная математика 5 класс )

Можно еще group_concat использовать, если известно, что во второй таблице мало данных на каждую строчку первой и таблицы не большие.
вот както так.

select a.*, B.* from Authors A left join (select B.Author, group_concat(B.field_needed SEPARATOR ’,’) as field_needed Books B group by Author) as B on B.Author = A.Id

Если есть условие значительно сокращающая выборку из A — то оно ставится и в А, и под группу, с джойном(два раза тоесть).
Смысл в том, что значительно сокращается передача из ядра БД в программу.

Но в любом случае надо смотреть схему.
В общем случае надо понимать такие вещи

1) практически любой запрос в базе отработает быстрее твоего кода с тем же уровнем оптимизации, просто потому, что не надо передавать данные и базу писали люди со скилом выше твоего.

2) если у тебя в базе больше 50тыс строк — все не вытаскивай.

3) делай explain. Очень желательно с количеством данных равном боевому.

4) современные компьютеры очень быстры. Преждевремнная оптимизация обычно не окупает железо за 5 лет. Очень большое количество кода живет меньше 5 лет. Лучше оставить в архитектуре точки для оптимизации в будущем, чем делать ее сразу.

ну и естественно
5) ты не являешься экспертом по базе данных, эксперт напишет запрос за в 10 раз меньшее время и с меньшим количеством косяков(применимо при наличии такого человека в команде).

Как вариант, не один класс, а класс Автор, класс Книга, коллекция Книги. Экземпляр Автора инициализируется без его книг. При обращении к полю коллекции Книги, идет запрос к базе и инициализация экземпляров книг. Можно еще по интервалу времени переинициализировать коллекцию, перечитывать книги из базы. Отложенная загрузка по надобности. Можно таким образом и не все данные по книге считывать, а только ту часть, что нужна на данный момент интерфейсу программы. Например, краткое содержание, внутренняя переменная, что соответствует такому же свойству класса Книга, будет считано из базы, если обратятся к нему и эта внутренняя переменная окажется = null.

Сначала извлечь все данные из таблицы авторов (select * from Authors)

Не все, а только нужные. Все извлечёшь только если они нужны тебе все.

а затем из таблица книг (select * from Books)

За это тебя выгонят ссаными тряпками на мороз. Ещё раз, ТОЛЬКО требуемые поля!

Данный способ меня настараживает тем, что данные по авторам и книгам извлекаются отдельными запросами, нет целостности/атомарности что-ли.

Схера ли тебя это должно настораживать? Целостность и атомарность засунь в жопу бюрократу, правда может не влезть, туда уже шаблоны проектирования засунуты и нормализация.
Да, ты можешь взять через LeftJoin, но ТОЛЬКО НУЖНЫЕ ПОЛЯ. Но всё равно книги ты возьмёшь отдельным запросом, автора отдельным.
Можешь взять и общим, если среднестатистически у тебя на автора приходится 2 книги. Сам по себе этот интерес у людей редкий, такие запросы прилетают нечасто, и нефиг их особо оптимизировать если у тебя сотня запросов в сутки.
Но в общем виде ты делаешь именно так, отдельным запросом. А если данных много — ещё и постраничный вывод.

В общем случае ложи хрен на лишнюю коммуникацию с базой данных. Там занимает время установка самого соединения. А когда оно уже создано — то лучше спрашивать данные по отдельности. Так ты избегаешь создания временных таблиц на самой базе, и спрашиваешь только то, что тебе надо в данный момент.

Доказывается экспериментом: сделай в цикле 1000000 раз и то и другое, засеки время, увидишь что простые действия оказываются и быстрее, так ещё и надёжнее. Надеюсь, про взаимные блокировки сложных запросов ты слышал?

PS. По-хорошему, целостность и атомарность у тебя контролируются на уровне приложения, а контроль на уровне базы избыточен. А потому убираем и лишние индексы, и foreign key в помойку, и прочее счастье проверяем на уровне приложения — там это более удобочитаемо и объясняемо комментами почему так. Кстати, кеширование запросов на уровне базы тоже отключаем, экономим гигабайты оперативы на ровном месте.

PS.

Если это сарказм и троллинг — то тонко, тонко... Но если это Java головного мозга, то печально :)

Особенно доставляет, что этот комент от ораклиста из банковой сферы.

Откуда мне знать на чём он пишет, жаба нигде не упоминалась.
По поводу целостности данных — статистически выгоднее держать её на уровне приложения, львиная доля баз данных тупо хранилище. В энтерпрайзе свои ананасы, там именно база логику держит, а вот в мелко-корпоративном секторе база по сути одна для сущности, вход к ней через одну дырку одним приложением, но зато логика такая что сам чёрт ногу сломит, так ещё и меняется постоянно, оставляя за собой авгиевы конюшни.

По-хорошему, целостность и атомарность у тебя контролируются на уровне приложения, а контроль на уровне базы избыточен. А потому убираем и лишние индексы, и foreign key в помойку, и прочее счастье проверяем на уровне приложения — там это более удобочитаемо и объясняемо комментами почему так. Кстати, кеширование запросов на уровне базы тоже отключаем, экономим гигабайты оперативы на ровном месте.

Надо же жопочасы какой-то деятельностью наполнять, понимаю

Данный способ меня настараживает тем, что данные по авторам и книгам извлекаются отдельными запросами, нет целостности/атомарности что-ли.

Делай это в пределах одной транзакции и будет тебе счастье в пределах уровня изоляции. А если данные долько добавляются, то вообще пофиг

... хотя, пишут depends. Так что ничего плохого в том, чтобы открыть транзакцию, не вижу. Ок

А что будет, если сделаешь в разных? Пока запрос выполняется, автор успеет книгу написать?
Правильно тут говорили, lazy наше всё, пишешь зависимость, а там само подтянется.

А если данные долько добавляются, то вообще пофиг

1.

как правильно извлечь связанные данные из базы данных.

Для начала, неплохо было бы изучить основные концепции той базы данных, из которой будете извлекать данные. Скорее всего, подобные вопросы прояснятся в ходе этого изучения.
2.

Я вижу два варианта:

Их больше. Описанные Вами варианты используются там, где нет глубокого понимания той базы данных, с которой работаете. Конечно, еще зависит от задачи, оговоримся, что сразу отсекаем какие-то простые случаи, когда с точки зрения производительности, совместимости, поддержки и т.д. в общем-то пофиг, как использовать БД. Хоть как записную книжку без транзакций...

Лично я предпочитаю подход, когда слой БД работает исключительно с хранимыми процедурами, в курсорах из которых возвращается вся необходимая информация. Таким образом, аппликация ничего не знает ни о каких SQL, JOIN, транзакциях и прочей специфической для БД терминологии. Но при этом подходе либо должен быть специалист, который создает/поддерживает такие процедуры, либо FullStack дев, который как минимум неплохо изучил п.1

3.

нет целостности/атомарности что-ли

Вот эти вопросы очень сильно зависят от той БД, которую используете (реляционные/nosql; версионные/блокировочные и т.д.). Поэтому сначала по-любому нужно выполнить п.1, тогда как минимум вопросы станут более осмысленными и углубленными.

4.
И лично я стараюсь пропагандировать генеральный подход: БД должна использоваться по потребности, но без излишества. Если можно вытянуть за 1 раз все данные — вытягиваем все. Если из этих данных будет использоваться только 1% — вытягиваем этот 1%. Если этот 1% будет вытягиваться 100500 раз в минуту — добавляем умный кеш на стороне аппликации и т.д.

Кстати, подход с хранимками сам по себе — неплохой, если само приложение эти хранимки и создает, и хранит их в гите, и если там нет сложной логики, кроме той, что относится непосредственно к получению или модификации данных. Но добавлять везде новые поля это конечно гемор еще тот.

Еще и позволяет защитить базу от удаления при поиске с sql injections )

А вы sql строками собираете?)

Ещё спроси где [ключ от квартиры, где девки лежат]

Последний раз такой изврат был году в 2000 с опытом меньше года. Помню колдфьюженовский select )))

То есть неплохой этот подход почти никогда

Увы, когда слой работы с данными лежит вне репы и ещё и меняется отдельно. То это жопа для maintainability.

имхо, лучше извлекать все данные одним запросом. Зачем делать 2 запроса, там где хватит одного.. И как писали ниже, ограничить поля необходимыми, вместо «*».
Не касается данного вопроса, но тут лучше связь многие к многим, так как у одной книги может быть несколько авторов ) Что выведет данный вопрос на новый уровень ))

Вот можно попробовать почитать, там похожая проблема www.thinktecture.com/...​1-queries-problem-in-2-0. Если в таблице авторов будет много полей и все нужно тащить, то, да, при join-е вытаскивать будео огромное количество redundant данных. Можно в одном запросе к бд вытащить все книги и всех авторой и склеивать уже на клиенте. Т.е. первый подход, но только один запрос к бд.

Ну, и вообще полезно взять ORM и посмотреть какой запрос она сгенерит для побной задачи.

Будет большое количество столбцов.

В селекте ты можешь указывать только те столбцы, которые тебе нужны, опратором * не обязательно пользоваться.

select A.Id, A.Firstname, B.Id, B.Title from Authors A left join Books B on B.Author = A.Id

С таким запросом ты вытянешь только столбцы Id и FirstName для автора и Id и Ttile для книги. Укажи нужные тебе столбцы и будет ок, если я правильно тебя понял.

Лучше пользоваться конечно же join опреатором, ты прав насчет атомарности, вполне себе может получиться так, что база выполнить между твоими двумя селектам удаление какого-то автора(несколько пользователей), и ты получишь книги удаленного автора.

Ну и стоить почитать поучить основы SQL тебе станет заметно проще.

Я написал «select *» чтобы не перечислять все колонки таблиц. Привел пример из головы. Извиняюсь если ввел в заблуждение. Но мне нужны все колонки таблица Authors и Books. И вопрос в том как лучше извлечь эти связанные данные, как это сделать не прибегая к подключению ORM.

ОРМ все равно генерит запросы на языке SQL, затем твое приложение отправляет в SQL сервер этот запрос и он выполняет этот запрос и потом твое приложение мапит возвращенный результат на твои обьекты(если у тебя ОРМ то смапит она сама). Никакой магии она не делает, хоть с ОРМ, хоть без ОРМ, все равно надо будет создавать запрос, разница лишь в том ты это делаешь руками или через ОРМ.

Если тебе нужны колонки, то почему проблема их вытаскивать? Нужны — добавил в селект, не нужны не добавил.

Можешь сделать два метода — один вытаскивает авторов и связанные данные, а второй только авторов и используй их по назначению — там где нужны данные о книгах и авторах — первый метод, где не нужны — второй.

Вообще проектирование Data Access Layer это довольно таки расширенная тема, и ее стоит изучать когда уже понимаешь как писать запросы, транзакции, уровни изоляции и прочие базовые знания реляционных СУБД.

Второе — никто всю таблицу не вычитывает, пока это действительно не понадобилось, обычно в таких селектах есть фильтры, чтобы SQL сервер отфильтровал лишние данные и вернул тебе именно то, что надо, если там будет несколько полей лишних, особо по перфомансу проблем не будет.

Прям в ответ на

P.S. друзі, що за снобизм

Ты показал просто эталон снобизма

Та нет, снобизма.

Перфоманс так не делается. Если ты хочешь перфоманс, ты берешь базу сравнимую по размерам с реальной и меряешь что будет быстрее. А так это уровень «ты че лох, джойнов не знаешь?».

А я вот читал, что джоин базы в таких простых вариантах всегда будет быстрее

А я читал что при доступе к базе по сетевому протоколу (а сокеты не всегда подходящий вариант) объём передаваемых данных таки имеет значение. Сомнения касательно применения джоина не должны возникать только в одном случае, когда релейшн 1 к 1.

А производительность запросов нужно измерять, а не надеяться на то что ты один раз где-то прочитал. Если она важна аж до миллисекунд. Если до миллисекунд не важна, то писать нужно так чтобы код был более понятным и поддерживаемым.

Если ты не видишь требований к перфомансу (да и вообще я по задаче не вижу к чему там требования), то почему это джойны лучше? Только потому что они существуют? Ему же потом все равно это по вложенным массивам раскладывать (массив авторов и у каждого автора массив книг).

И адово высокие затраты на малейшее изменение. Одной только бюрократии на месяцы. А тесты...

Не все же в банках работают. Закоммитил, потестил на тестовой базе — и ***к в продакшен.

А че, давай не только джоины закодим но и еще половину функционала базы, всегда так делай.

В базе как бы есть инструменты для валидации. Нафига делать её ещё и в приложении, причем в случае веба в двух местах (бек/фронт)?

Нет, коллега, перформанс так не делается. Надо брать не сравнимую по объему базу (кстати, она должна быть сравнима и по железу), а включать трассировку и анализировать планы/трейсы двух запросов. Но я никак не могу это посоветовать ТС, т.к. это будет выглядеть как троллинг и издевательство...

А кто вообще говорил про перфоманс? В основной массе проектов, 9999 из 10 000, достаточно грамотного индексирования и умения с этими индексами работать. Как минимум, понимания что работаешь не с таблицами, а с индексами.

К тому моменту когда реально требования к перфомансу вырастут, уже будет повод переписать многое, если не всё. Включая схемы данных и способ их хранения.

Гм, а причем тут с ОРМ или без? С ОРМ вам придется отвечать на теже самые вопросы. В чем великий смысл гарантировать целостность для вашей задачи? Что будет если новая книга или новый автор добавятся через пол секунды после вычитки данных?

А смысл без ORM, если нужно получить то, что она и делает? Очень надо написать свой велосипед или это ТЗ тестового такое? :)

нет целостности/атомарности что-ли.

А разве тут она нужна?

1) Сначала извлечь все данные из таблицы авторов (select * from Authors), а затем из таблица книг (select * from Books). Далее привести эти данные в нужный вид. Данный способ меня настараживает тем, что данные по авторам и книгам извлекаются отдельными запросами, нет целостности/атомарности что-ли.

В пределах одной транзакции MVCC гарантирует целостность.

2) Извлечь сразу все связанные данные (select * from Authors A left join Books B on B.Author = A.Id), а потом преобразовать в массив нужного класса. Данный способ не нравится тем, что полные данные автора будут у каждой книги. Будет большое количество столбцов.

Даже не буду это комментировать. Быстро читать про виды джойнов.

Вот и дожили, господа, когда чтобы сделать JOIN обращаются на форум. Дай, угадаю, ты вайтишник, до того как начать писать код работал охранником в АТБ?

Вот и дожили, господа, когда чтобы сделать JOIN обращаются на форум. Дай, угадаю, ты вайтишник, до того как начать писать код работал охранником в АТБ?

Вообще-то ТС пришел с адекватным вопросом про подход, а не просил «где в моем коде ошибка».
Если ТС вайтишник, то это вполне достойная тема для начинающего, в отличии от большенства вайтишников.

Меня триггерит, что из за ОРМ разрабы не знают ничего про SQL, хотя годами, каждый день работают с реляционками.

Меня триггерит, что из за ОРМ разрабы не знают ничего про SQL, хотя годами, каждый день работают с реляционками.

О ужас!
Сядьте на стул с подлокотниками. И хорошо в них вцепитесь руками!
Хорошо держитесь?
.
.
.
.
.
.
.
Они еще и про регистры и кешлайны не в курсе, а что такое тригер или шифратор/дешифратор так вообще мало кто слышал.

Коментар порушує правила спільноти і видалений модераторами.

Так и есть! Убери хайповые фреймворки и 99% айтишников сядут в лужу.

ну не так чтоб 99, но процентов 70 вполне возможно, особенно из вошедших 5-10 лет назад, когда изучать внутренности уже было не особо модно

ТС стал жертвой идиотских книг, где учат плодить лики абстракции, создавая на каждую сущность по репозиторию вроде BookRepository, AuthorRepository и дергать в них хибернейт. Но реальная жизнь вообще не пересекается с этими книгами даже на таких элементарных сценариях как у ТС.

Угу. А так же жертвой того, что все пользуют ORM и не знаю как решить задачу без него.

Смію зауважити, що я в’їхав у JOIN відносно пізно, але до того оперував вкладеними селектами, не збавляючи швидкості. Я не захищаю цей підхід, але я принаймні мав його на увазі і навіть використовував.

ТС мог бы почитать документацию на свой сервер БД и не беспокоить форум по пустякам.

ТС мог бы почитать документацию на свой сервер БД и не беспокоить форум по пустякам.

С каких пор в доках по СУБД пишут как лучше организовывать ДАО которые могут их использовать?

Это базовый скил — скл, знание нужное всем бэкендерам

лол просто лол

НоСКЛ, даталейки (где скл на уровне «вер поле=значение»), числодробилки, ИоТ

Не зная скл и принципы его работы и реляционки — ты 99% не сможешь сделать осознанный выбор

СКЛ и принципы реляционных бд — это разные вещи. Как знание джоинов или групбай вам помогло вибрать между вайдколамн, кей-велуе и документной бд?

2. Иот не хранят тонны статистики?))

Вот именно, тонны. И РДБМС — далеко не первый выбор. Снова же там «скл на уровне „вер поле=значение“»

3. даталейки не зная скл?! Удачи

Нука раскажите как ви к ХМЛ-файлам на С3 будете делать запроси используя СКЛ и джойнить это с дампами ФИКС-фида? Снова же там «скл на уровне „вер поле=значение“»

Вот именно, тонны. И РДБМС — далеко не первый выбор. Снова же там «скл на уровне „вер поле=значение“»

Это до поры, до времени, пока не захочется агрегаций и прочего олапа. И тут начинается самое интересное.

Хотя если кейс совпадает с идеальным для многих nosql (писать, никогда не читать), то проблем нет, почти нет.

Я написал «select *» чтобы не перечислять все колонки таблиц. Привел пример из головы. Извиняюсь если ввел в заблуждение. Но мне нужны все колонки таблица Authors и Books. И вопрос в том как лучше извлечь эти связанные данные, как это сделать не прибегая к подключению ORM.

Нет не охранник, но и не чистокровный айтишник. Весь прикол в том, что мне на данный вопрос из моего окружения (не большого) ответ дать не смогли. Все используют в лоб фреймворки и орм и даже не знают как что сделать без них.

Я написал «select *» чтобы не перечислять все колонки таблиц. Привел пример из головы. Извиняюсь если ввел в заблуждение. Но мне нужны все колонки таблица Authors и Books. И вопрос в том как лучше извлечь эти связанные данные, как это сделать не прибегая к подключению ORM.

Суть вопроса в том, что в первом варианте делается два запроса к таблице авторов, а затем к таблице книг. И меня напрягает, что данные в таблице авторов могут поменяться (добавиться или удалиться), пока моя программа начала делать запрос к таблице книг. Чтобы избежать этого есть второй вариант — заджойнить обе таблицы и сделать один запрос, он явно будет атомарным, но мы получаем большое количество колонок (мне нужны все колонки из таблицы авторов и книг). Вы знаете как лучше поступить не прибегая каким-либо ORM’ам?

Мужик, поучись читать, реально. Он же написал

Но мне нужны все колонки таблица Authors и Books

При чем здесь замена звездочки на список столбцов если ему нужны все столбцы

И меня напрягает, что данные в таблице авторов могут поменяться (добавиться или удалиться), пока моя программа начала делать запрос к таблице книг.

Это маловероятный сценарий на самом деле, там между запросами миллисекунды будут. И это ничего не сломает, ты просто отобразишь данные устарешие на эти миллисекунды

Ты ведь написал правильный ответ с Джоином

Правильного ответа здесь нет или недостаточно информации для него.

Правильный ответ т.е. в терминах данного вопроса правильное инженерное решение это то решение которое наиболее целесообразно с точки зрения негативного и позитивного влияния решения на конечные цели задачи. В данном конкретном случае оба решения одинаково хороши/плохи в контексте предоставленных требований. Все остальное фантазии на тему.

Ты так рассуждаешь будто ТС тупо дампить результирующую табличку в эксель/хтмл будет. В то время как ТС говорит,

Я бы хотел извлечь эти данные в класс, который содержит все данные по автору и список всех его книг.

Таким образом хоть и будут в выдаче данные связаны, ТСу от этого ни холодно ни жарко, всё равно преобразовывать в желаемую структуру. Так разница?

Не, ну он конечно должен был в иллюстративном коде перечислить 10-20 полей

Можно не извлекать все колонки, а только нужные. В приличных программах «select *» не используют

Я написал «select *» чтобы не перечислять все колонки таблиц. Привел пример из головы. Извиняюсь если ввел в заблуждение. Но мне нужны все колонки таблица Authors и Books. И вопрос в том как лучше извлечь эти связанные данные, как это сделать не прибегая к подключению ORM.

Руками. Джойном, перечисляя все нужные колонки.

То, что ты спрашиваешь — ты неправильно задаешь вопрос.
Твой вопрос на самом деле касается того, как замапить избыточные записи авторов, которые тебе дал джойн, на книги.

Грубого говоря, джойн вернет тебе

List<Author, Book>
,
где в авторах, очевидно, есть дубликаты. Тебе же надо сделать маппинг в
Map<Author, List<Book>>
,
где дубликаты авторов убраны, а каждому автору соответствует список книг.
Сиквелем этого не сделать, потому что ты спрашиваешь про то, как представить это в приложении, а не в базе.
Поэтому и маппинг
List<Author, Book>  --> Map<Author, List<Book>> 
Придется делать внутри приложения, изначально выполнив джойн.

ORM да, может сделать это за тебя.

А базовом сценарии такой маппинг можно сделать тупой итерациией по изначальному

List<Author, Book>
и добавляя в мапу каждую запись, находя уже добавленного автора, или добавляя нового, если такой автор еще не встречался в выборке.

Еще вариант: вычитать данные автора, а для списка его книг сделать ленивую загрузку, т е тянуть книги только тогда, когда к ним попытаются обратиться.

Но не совсем понятно назначение системы еще, сначала написано

Я бы хотел извлечь эти данные в класс, который содержит все данные по автору и список всех его книг

т е заполняем объект для конкретного автора, чтобы с ним работать
а в решениях и 1 и 2

select * ...

то есть сразу вычитываем все данные из БД в набор объектов, зачем?

Если нужно таки вычитать все данные из БД возможен в принципе вариант 2, но читать не сразу все, а пачками.

Я написал «select *» чтобы не перечислять все колонки таблиц. Привел пример из головы. Извиняюсь если ввел в заблуждение. Но мне нужны все колонки таблица Authors и Books. И вопрос в том как лучше извлечь эти связанные данные, как это сделать не прибегая к подключению ORM.

Да, мне нужны все данные из двух таблиц. Как это вычитать все данные, но не сразу а пачками? Объясните пожалуйста.

Я тоже чуть криво написал, вопроc не столько в «select *», столько в отсутствии фильтров, там нет where, то есть вычитываются все данные (именно строки, столбцы — даже неважно) из обоих таблиц. Вопрос — зачем такое надо?

Если тут

Да, мне нужны все данные из двух таблиц.

имеются ввиду именно все строки, то подойдет запрос с join (вариант 2) с limit и offset (где указывается, какую порцию данных брать), который будет вызываться в цикле со сдвигом смещения каждую итерацию.
Т е например пачками по 100:
Select ... from authors left join books on ... limit 100  Select ... from authors left join books on ... limit 100 offset 100  Select ... from authors left join books on ... limit 100 offset 200  ...

Если у тебя нет реально тяжёлых данных, то limit можешь оставить кодописцам 90х, тяни всё, не ошибёшься. Не подохнет клиент от лишних 5 килобайт, инфа сотка.

Затем что я человек, я не только учу плохому, я приказываю машинам пахать как папа Карло. И не чтобы отрендерить какое-то скромное окошко, спалив 200мб оперативы, а чтобы дать пользователю чуть-чуть больше данных, чем он хотел.

И да, чтобы посетитель тыкал «купить» чуточку чаще

Да. А потом сраный gmail(просто почтовый клиент) загружается 30-90 секунд. Спасибо, но нет.

А зачем тебе сраный gmail клиент? Поставь себе что покраще. А гмыло, даже если не удаляется, прикинувшись системным, сошли на SD-карту и отвяжи от событий через Autostarts.

Тут же нужно очень точно понимать что будет с базой на проде. Таким подходом люди сталкиваются с ситуацией когда написали приложение, сдали заказчику, а на проде оно дико тормозит или вообще упало. Потому что в деве 100 строк в базе, а на проде 100к

Вообще-то с этого понимания и нужно начинать. А не вот эти все теории разводить. Правильно — это оставить механизм лимитирования, но зарыть его в настройках. Кому надо — увидит, кому пока не надо (абсолютному большинству) — тем не будет глаза мозолить.

Это как с поиском по мере набора текста в строке. Казалось бы, элементарщина, прикрутил и готово. Но эта самая «элементарщина» множит на ноль полезность мобильных приложений, не имея на борту банальнейшего пинга до того как включать плюшки. Это можно было бы простить джунам с проектами на 10 юзеров, но мать вашу, Гугл Мапс...

Зазвичай драйвер БД і так віддає дані пачками, можна просто забирати (fetch) дані потрібними порціями.

Как лучше сделать?

Чтобы ответить на этот вопрос нужно понимать «зачем это надо». Какую бизнес проблему вы решаете? Какую техническую проблему вы решаете?
Еще важно что является корневой сущностью? Являются ли Books и Authors сущностями или один из них не имеет смысла без бругого?

Оба варианты валидные. Правильно спрятав их за репозирорием, вы можете сделать любой из них, а потом если надо переделать.

Данный способ меня настараживает тем, что данные по авторам и книгам извлекаются отдельными запросами, нет целостности/атомарности что-ли.

За целосность отвечают не запросы а транзакции, поэтому то что вы описали не проблема.

Данный способ не нравится тем, что полные данные автора будут у каждой книги. Будет большое количество столбцов.

В целом валидный поинт, но иногда 1 запрос может работать быстрее.

Я написал «select *» чтобы не перечислять все колонки таблиц. Привел пример из головы. Извиняюсь если ввел в заблуждение. Но мне нужны все колонки таблица Authors и Books. И вопрос в том как лучше извлечь эти связанные данные, как это сделать не прибегая к подключению ORM.

Меня в первом варианте смущает то, что сделав запрос по авторам, а потом делая запрос по книгам, с таблицей автором могло что-то произойти (добавиться или удалиться автор).

что сделав запрос по авторам, а потом делая запрос по книгам, с таблицей автором могло что-то произойти
За целосность отвечают не запросы а транзакции, поэтому то что вы описали не проблема.

Если вы не в курсе что такое транзакции, то почитайте, это есть в большенстве докуменаций и во всех нор книгах по БД.
К слову, чаще всего ОРМ, делают так как ви описали в пункте 1 с оговоркой что они выбирают книги __для каждого автора__, но есть возможность сделать 1 запросом (ваш вариант № 2).

Но мне нужны все колонки таблица Authors и Books

Если вам нужно работать с сущностью Автор и иногда брать его книги, то первый вариант ок.
Если вам нужно работать с сущностью Книга и иногда брать ее автора, то второй ваниант выглядит интереснее.
Но тут проблема, вы уверены что у книги 1 автор? :) (я так понимаю что мы обсуждаем учебную задачу)

Да, задача учебная (для себя). Видим я не до конца понимаю транзакцию... Вот создал я подключение к БД, сделал select всех данных из таблица авторов, далее второй отдельный запрос на получение всех данных по книгам произошел через минут 5 (допустим моя программа подвисла или еще что). Разве полученные мной данные из таблицы авторов не могли устареть?

через минут 5 (допустим моя программа подвисла или еще что). Разве полученные мной данные из таблицы авторов не могли устареть?

Могли.
Тут еще интересный вопрос: а где вы будете хранить результаты запросов в течении этих 5 минут? И сколько каких ресурсов сам потребуется чтобы поддержать работу 10-100-1000 одновременных пользователей?

Вот такие базовые вещи как понимание транзакций, лучше читать в книгах, посмотрите видио на ютюбе.

Как вариант
github.com/...​ange/Dapper#multi-mapping
ЗЫ. Даже не поинтересовался что за язык у тебя там?

Я написал «select *» чтобы не перечислять все колонки таблиц. Привел пример из головы. Извиняюсь если ввел в заблуждение. Но мне нужны все колонки таблица Authors и Books. И вопрос в том как лучше извлечь эти связанные данные, как это сделать не прибегая к подключению ORM.

Язык тут не важен. Возьмите что хотите C# / Java / Python / ect. Как правильно извлечь данные из двух таблиц?

Підписатись на коментарі