如何在活动记录中编写子查询?

时间:2018-06-01 10:03:13

标签: ruby-on-rails ruby postgresql activerecord

我有两个表usersposts,他们有has_many的关联。我想在单个查询中获取usersposts的详细信息。我能够管理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 :(得分:3)

如果您只想要那些特定的列而不是其他任何内容,那么这将起作用

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_idpost_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,其中包含两种有用的方法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,其中键是列名,以及该记录的值或行值。

答案 1 :(得分:2)

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在SQL
  • 中包含INNER JOIN语句
  • 我们使用includes加载内存中的所有posts

答案 2 :(得分:1)

  

我有两个表用户和帖子,他们有关联   有很多。我想在一个单独的内容中获取用户和帖子的详细信息   查询。

可以使用像

这样的包含来完成
users = User.includes(:posts).where({posts: {user_id: [1,2,3]}})
根据您的要求,您可以使用

其他eager_loadpreload,以获得更多https://blog.arkency.com/2013/12/rails4-preloading/