4

Q:1つのクエリですべての投稿と関連するコメントを選択し、コメントの関係をp.commentsにロードしますが、コメントからタイトルのみを選択します

役職

#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

必要なもの:1つのクエリですべての投稿と関連するコメントを選択し、コメントの関係をp.commentsにロードしますが、コメントからタイトルのみを選択します。

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

=>SELECT postsidAS t0_r0 、poststitleAS t0_r1 、poststextAS t0_r2 、postscreated_atAS t0_r3 、postsupdated_atAS t0_r4 、commentsidAS t1_r0 、commentstitleAS t1_r1 、commentstextAS t1_r2 、commentspost_idAS t1_r3 、commentscreated_atAS t1_r4 、commentsupdated_atAS t1_r5 FROM postsLEFT commentsOUTERJOINON commentspost_id= postsid

selectを完全に無視します。

では、参加してみましょう。

Post.joins(:comments).select('posts.*, comments.title')

=> SELECT posts。*、comments.title FROM postsINNERJOINON 。= 。commentscommentspost_idpostsid

より良いですが、テーブルエイリアスが必要です-Railsはリレーションを処理しません(なぜですか?)

posts = Post.joins(:comments).select('posts.*, comments.title as comments_title').first

=> SELECTposts。*、comments.title ascomments_title FROM postsINNERJOINON 。= 。commentscommentspost_idpostsid

posts.first.comments.loaded? # false
posts.first.comments_title # "Comment 1.1"

OK、ARelを試してみましょう

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
)

=>SELECT postsidpoststitlecommentsidcommentstitle内部posts結合commentsからcommentspost_id= postsid

同じ話-ActiveRecordはリレーションを処理しません。

助言がありますか?

UPD

  1. この質問が直接的な解決策ではない場合は、いくつかのARメソッドを使用して結果のクエリを変換する方法があるかもしれません。なぜなら、ARはARelを使用し、それに対してfind_by_sqlを呼び出してから、魔法をかけます。これは、いつ実行されたかを見つけることができず、ビオラ:生成されたエイリアスと読み込みの関連付けです。

  2. たぶん、1対1の関係の解決策はありますか?

    投稿=Post.select('posts.id、posts.title、comments.id、comments.title')。includes(:comment)posts.first.comment.loaded?#true

=>SELECT postsidAS t0_r0 、poststitleAS t0_r1 、poststextAS t0_r2 、postscreated_atAS t0_r3 、postsupdated_atAS t0_r4 、commentsidAS t1_r0 、commentstitleAS t1_r1 、commentstextAS t1_r2 、commentspost_idAS t1_r3 、commentscreated_atAS t1_r4 、commentsupdated_atAS t1_r5 FROM postsLEFT commentsOUTERJOINON commentspost_id= postsid

4

3 に答える 3

3

説明したすべてのシナリオが意図したとおりに機能しているため、提案はありません。

  • :includes=>積極的な読み込み
  • :joins=>積極的な読み込みではありません

したがって、コメントを直接ロードする場合は、includesを使用する必要があり、結合は機能しません。

次に、SQLクエリで使用される特定の選択構文のため、積極的な読み込みが機能します。つまり、インクルードと組み合わせて使用​​すると、指定した選択は無視され、デフォルトですべての列がロードされます。

探している機能を追加する最新の試みは、このチケットに記載されていると思います。このチケットでは、includesの呼び出しにexceptパラメーターを追加することで解決されています。しかし、まだ何の反応も得られていないようです。

個人的には、includesで指定した最初の例を使用しますが、selectをスキップして、comment.textがロードされているという事実を確認します。私が考えることができる他のシナリオよりも優れています。

于 2011-02-01T14:16:30.920 に答える
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

少しハッキーですが、うまくいくようです。ActiveRecordは、さらに複雑なクエリを実行するための接続属性も公開します。http://apidock.com/rails/ActiveRecord/Base/connection

于 2013-01-18T23:55:01.060 に答える
-1

解決策の1つは、ActiveRecortではなくDataMapperに切り替えることです。私にとってはARよりもはるかに優れたormです。 ActiveRecordの代わりにDataMapperを使用する

于 2011-03-20T15:00:09.697 に答える