Как написать подзапрос в активной записи? - PullRequest
0 голосов
/ 01 июня 2018

У меня есть две таблицы users и posts, и они имеют ассоциацию has_many.Я хочу получить данные как users, так и posts в одном запросе.Я могу управлять запросом sql, но не хочу использовать необработанный запрос в коде (используя метод execute), так как я думаю, что это довольно просто и может быть написано с использованием активной записи.

Вот SQL-запрос

SELECT a.id, a.name, a.timestamp, b.id, b.user_id, b.title
FROM users a
INNER JOIN (SELECT id, user_id, title, from, to FROM posts) b on b.user_id = a.id
where id IN ( 1, 2, 3);

Я думаю, includes здесь не поможет, потому что я имею дело с большими данными.Кто-нибудь может мне помочь?

Ответы [ 3 ]

0 голосов
/ 01 июня 2018

У меня есть две таблицы пользователей и сообщений, и у них есть ассоциация has_many.Я хочу получить информацию о пользователях и сообщениях в одном запросе.

можно сделать с помощью таких включений, как

users = User.includes(:posts).where({posts: {user_id: [1,2,3]}})

, другие eager_load и preload, которые вы можетеиспользовать в соответствии с вашими требованиями, для более https://blog.arkency.com/2013/12/rails4-preloading/

0 голосов
/ 01 июня 2018

Если вам нужны только эти конкретные столбцы и больше ничего, тогда это будет работать

User.joins(:post)
 .where(id: [1,2,3])
 .select("users.id, users.name, users.timestamp, 
          posts.id as post_id, posts.user_id as post_user_id, 
          posts.title as post_title") 

Это вернет ActiveRecord::Relation из User объектов с виртуальными атрибутами для post_id, post_user_id (Не уверен, зачем вам это нужно, так как вы уже выбрали users.id) и post_title.

Полученный запрос будет

   SELECT users.id, 
          users.name, 
          users.timestamp, 
          posts.id as post_id,
          posts.user_id as post_user_id, 
          posts.title as post_title
   FROM users
   INNER JOIN posts on posts.user_id = users.id
   where users.id IN ( 1, 2, 3);

Обратите внимание, у вас может быть несколько Userобъекты, по одному на каждый Post, так же, как это делает запрос SQL.

Вы можете выполнить свой точный запрос, используя строковую версию joins, например,

User.joins("INNER JOIN (SELECT id, user_id, title, from, to FROM posts) b on b.user_id = users.id")
 .where(id: [1,2,3])
 .select("users.id, users.name, users.timestamp, 
          b.id as post_id, b.user_id as post_user_id, 
          b.title as post_title") 

Дополнительно, чтобы избежать некоторыхиз накладных расходов вы можете использовать arel вместо, например,

 users_table = User.arel_table
 posts_table = Post.arel_table

 query = users_table.project(Arel.star)
           .join(posts_table)
             .on(posts_table[:user_id].eq(users_table[:id]))
           .where(users_table[:id].in([1,2,3]))
 ActiveRecord::Base.connection.exec_query(query.to_sql)

Это вернет ActiveRecord::Result с 2 полезными методами columns (выбранные столбцы) и rows.Вы можете преобразовать это в Hash (#to_hash), но учтите, что любые столбцы с повторяющимися именами (например, id) будут перезаписывать друг друга.

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

query =  users_table.project(
    users_table[:id],
    users_table[:name],
    users_table[:timestamp],
    posts_table[:id].as('post_id'),
    posts_table[:user_id].as('post_user_id'),
    posts_table[:title].as('post_title')
).join(posts_table)
   .on(posts_table[:user_id].eq(users_table[:id]))
.where(users_table[:id].in([1,2,3]))

ActiveRecord::Base.connection.exec_query(query.to_sql).to_hash

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

0 голосов
/ 01 июня 2018
users = User.joins(:posts).includes(:posts).where(id: [1, 2, 3])

Даст вам всех пользователей с их сообщениями.

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

first_user_posts = users.first.posts # this will not make additional DB queries as you used includes and data is already added

  • Мы используем joins, чтобы иметь INNER JOIN оператор в SQL
  • Мы используем includes, чтобы загрузить все posts в памяти
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...