问:在一个查询中选择所有帖子和相关评论,将评论关系加载到p.com中,但只从评论中选择标题
发布
#db
create_table :posts do |t|
t.string :title
t.text :text
t.timestamps
end
#modlel
class Post < ActiveRecord::Base
has_many :comments
end
注释
#db
create_table :comments do |t|
t.string :title
t.text :text
t.integer :post_id
t.timestamps
end
#model
class Comment < ActiveRecord::Base
belongs_to :post
end
我需要什么: 在一个查询中选择所有帖子和相关评论,并将评论关系加载到p.com中,但只从评论中选择标题:
posts = Post.includes(:comments).select("posts.*, comments.title")
posts.each do |p|
puts(p.title)
puts(p.text)
p.comments.each do |c|
puts(c.title)
end
end
如果第一部分由.includes:
完成posts = Post.includes(:comments).select('posts.*, comments.title')
posts.first.comments.loaded? # true
=&GT;选择posts
。id
AS t0_r0,posts
。title
AS t0_r1,posts
。text
AS t0_r2,posts
。{ {1}} AS t0_r3,created_at
。posts
AS t0_r4,updated_at
。comments
AS t1_r0,id
。comments
AS t1_r1,{ {1}}。title
AS t1_r2,comments
。text
AS t1_r3,comments
。post_id
AS t1_r4,comments
。{{1 AS t1_r5 FROM created_at
LEFT OUTER JOIN comments
ON updated_at
。posts
= comments
。comments
完全忽略了选择。
好的,让我们尝试加入:
post_id
=&GT; SELECT posts。*,comments.title FROM posts
INNER JOIN id
ON Post.joins(:comments).select('posts.*, comments.title')
。posts
= comments
。comments
更好,但我们需要表别名--Rails不处理关系(为什么?)
post_id
=&GT; SELECT posts。*,comments.title as comments_title FROM posts
INNER JOIN id
ON posts = Post.joins(:comments).select('posts.*, comments.title as comments_title').first
。posts
= comments
。comments
post_id
好的,让我们试试ARel
posts
=&GT;选择id
。posts.first.comments.loaded? # false
posts.first.comments_title # "Comment 1.1"
,p = Post.arel_table
c = Comment.arel_table
posts = Post.find_by_sql(
p.join(c).on(c[:post_id].eq(p[:id])) \
.project(p[:id],p[:title],c[:id],c[:title]).to_sql
)
。posts
,id
。posts
,title
。comments
FROM { {1}}内部加入id
开启comments
。title
= posts
。comments
相同的故事 - ActiveRecord不处理关系。
有什么建议吗?
UPD
如果这个问题没有直接的解决方案,也许有一种方法可以使用一些AR方法来转换结果查询。因为,AR使用ARel,然后在其上调用find_by_sql,然后做一些魔法,我无法找到执行的时间和中提琴:中提琴:生成的别名和加载关联。
也许有一对一关系的解决方案?
posts = Post.select('posts.id,posts.title,comments.id,comments.title')。includes(:comment) posts.first.comment.loaded? #true
=&GT;选择comments
。post_id
AS t0_r0,posts
。id
AS t0_r1,posts
。id
AS t0_r2,posts
。{ {1}} AS t0_r3,title
。posts
AS t0_r4,text
。posts
AS t1_r0,created_at
。posts
AS t1_r1,{ {1}}。updated_at
AS t1_r2,comments
。id
AS t1_r3,comments
。title
AS t1_r4,comments
。{{1 AS t1_r5 FROM text
LEFT OUTER JOIN comments
ON post_id
。comments
= created_at
。comments
答案 0 :(得分:3)
我担心我没有任何建议,因为您描述的所有场景都按预期工作。
因此,如果您希望直接加载注释,则必须使用包含,连接将永远不会有效。
接下来,由于SQL查询中使用了某些选择语法,因此需要加载。换句话说,当与includes一起使用时,将忽略您指定的选择,默认情况下将加载所有列。
我认为最近尝试添加您正在寻找的功能是in this ticket,它通过在include调用中添加except参数来解决。但它似乎没有得到任何回应。
就我个人而言,我会选择你使用includes指定的第一个例子,但是跳过select并使用了加载comment.text的事实。比我能想到的任何其他场景更好。
答案 1 :(得分:0)
您还可以通过执行以下操作来使用具有活动记录的原始SQL查询:
post_comment = Post.find_by_sql("SELECT p.*, c.title AS comment_title FROM posts p JOIN comments c ON p.id=c.post_id").first
post_comment.comment_title
有点hacky但似乎有效。 ActiveRecord还为您公开连接属性,以执行更复杂的查询。 http://apidock.com/rails/ActiveRecord/Base/connection
答案 2 :(得分:-1)
其中一个解决方案是切换到DataMapper而不是ActiveRecort。那对我而言,AR更好。 Use DataMapper instead of ActiveRecord