ActiveRecordはレヌキ、リレヌション、むンデックスに぀いお少しです

痛みを䌎うこずに぀いおお話したいず思いたす。䞀般的にはARで、特にリレヌションで働くこずです。 簡単にあなたの人生を台無しにしお、コヌドを遅くお倧食いにするこずができる暙準的な庭の補品に察しお譊告しおください。 物語は、同じ流出のRails 3.2ずActiveRecordに基づいおいたす。 もちろん、Rails 4には新しくお䟿利なものがたくさんありたすが、それを切り替える必芁がありたす。ずにかく基盀は同じです。



ほずんどの堎合、この資料は初心者向けです。ARを䜿甚する堎合、䜜成者がActiveRecordオブゞェクトやその他の射撃肢の圢でテヌブル党䜓の内容をメモリに抜出するのは非垞に苊痛だからです。 Zenを孊んだ開発者にずっお、このトピックは圹に立぀ずは思われたせん。圌らは自分の䟋ず教蚓でそれを補完するこずしかできたせん。






圌らは䞖界に䜕回繰り返したしたか...



Relation および䞀般的なActiveRecordオブゞェクトで䜜業を開始した堎合、1぀のこずを明確に提瀺する必芁がありたす。遞択を「具䜓化」するポむント、぀たりSQLク゚リの構築を停止するポむントです。 蚀い換えるず、デヌタがフェッチされ、メモリ内の凊理に進むずきです。 なぜこれが重芁なのですか はい、それは厄介だからです



Product.all.find{|p| p.id == 42}
      
      





サヌバヌをハングアップさせ、すべおのRAMを取埗し、さらに倚くの汚いトリックを実行できたす。 そしお同じこずですが、蚀い換えれば



 Product.find(42)
      
      





結果なしですぐに動䜜したす。 したがっお、 findずfindはたったく同じものではありたせん なんで はい、最初のケヌスではProduct.allず蚀っお足で撃ちたした。これは、 補品テヌブルのコンテンツ党䜓を抜出し、各行でARオブゞェクトを構築するこずを意味するため、それらから配列を䜜成し、クラスメ゜ッドであるfindをりォヌクスルヌするためです配列 䞀般的に、 findは Enumerableからのものですが、これらはすでに詳现です。 2番目のケヌスでは、すべおがはるかに優れおいたす。findはARメ゜ッドであり、 pkで怜玢するように蚭蚈されおいたす。 ぀たり、リク゚ストを生成したす



 SELECT * FROM products WHERE products.id = 42;
      
      





それを実行し、1行すべおを受け取りたす。



良い点ず悪い点



ここで、ARを扱うこずが倧きな責任である理由がわかったので、足元を撃たないようにしたしょう。 これは非垞に簡単です。ARが提䟛するメ゜ッドを䜿甚する必芁がありたす。 ここにありたす where、select、pluck、includes、joins、scoped、unscoped、find_eachなど、ドキュメントたたは隣接するハブにありたす。 ただし、䜿甚しない方が良いのは列挙が非垞に難しく、同時に非垞に簡単です。他のすべおのメ゜ッドを䜿甚するのは望たしくありたせん。残りのほずんどすべおのメ゜ッドが関係を配列に倉換するためです。



シンプルなレシピ



今、私はいく぀かの暙準を提䟛したすが、生掻を楜にするデザむンはあたりありたせんが、しばしば忘れられたす。 しかし、読者に質問する前に、has_many関数を思い出しおください。 知っおいるパラメヌタヌず、積極的に䜿甚しおいるパラメヌタヌに぀いお考えおください。 あなたの心にそれらをリストし、それらを数えたす...そしお今質問は次のずおりですあなたはそれらのうちの䜕人が実際に知っおいたすか



答え
Rails3に24個、Rails4に12個。 12pcsの違いは、 where、groupなどのメ゜ッドず、ハッシュではなくブロックでRails4に枡される玔粋なSQLを操䜜するメ゜ッドによっお生じたす。


なぜこれを聞いたのですか はい、あなたのレベルを非垞に倧たかに評䟡し、ほずんどのオプションを知っおいるなら、以䞋はあなたに新しい知識をもたらす可胜性が䜎いず蚀うために。 この評䟡は非垞に条件付きです。したがっお、芪愛なる読者は、ずんでもない/支持できない/奇劙/など必芁なこずを匷調するように思えおも、あたり怒らないでください。



レシピ回数



それでは、順番に行きたしょう。 update_attributesずupdate_attribute たたはすべおではないに぀いおは誰もが知っおいたす。 最初の-怜蚌ずコヌルバックの呌び出しでフィヌルドを倧量に曎新したす。 面癜くない。 2番目-すべおの怜蚌をスキップし、コヌルバックを起動したすが、遞択した1぀のフィヌルドの倀のみを曎新できたす䞀郚の人にずっおは、 保存怜蚌falseの方が䌌おいたす  しかし、䜕らかの理由で、圌らはしばしばupdate_columnずupdate_allを忘れたす。 このメ゜ッドは、怜蚌ずコヌルバックの䞡方をスキップし、前戯なしでデヌタベヌスに盎接曞き蟌みたす。



レシピ番号2



コメントは玠晎らしいタッチ方法を思い出させたした。 圌らはしばしば圌のこずを忘れお、



 @product.updated_at = DateTime.now @product.save
      
      





たたは



 @product.update_attribute(:updated_at, DateTime.now)
      
      





ただし、この目的のためには、次のようにするほうが簡単です。



 @product.touch(:updated_at) #      
      
      





さらに、 touchには独自のafter_touch コヌルバックず、オプションtouchが belongs_toメ゜ッドにありたす。



正しく反埩する方法



ハブはすでにfind_eachに぀いお話しおいたしたが、もう䞀床蚀及するしかありたせん。



 product.documents.map{
}
      
      





そしおそれらは同圢であり、どこよりも少し倚く芋぀かりたした。 Relationで䜿甚される埓来のむテレヌタには、1぀だけ問題がありたす。それらは、デヌタベヌスからすべおを䞀床に匕き出したす。 そしおこれはひどいです。 察照的に、デフォルトでは、 find_eachは䞀床に1000個を持ち、問題ありたせん



UPDコメントで既に述べたように、raw-sqlに明瀺的に投圱されおいないすべおのメ゜ッドはto_aに委任されたす。これが、ク゚リ党䜓がメモリにフェッチされ、その操䜜がDB偎ではなくRuby偎にある理由です。



default_scopeに関するヒント



default_scopeの内容をブロックでラップしたす。 䟋



 default_scope where(nullified: false) # ! default_scope { where(nullified: false) } # 
      
      





違いは䜕ですか 最初のオプションはサヌバヌの起動時に実行され、 無効化されたフィヌルドがデヌタベヌスになかった堎合、サヌバヌは起動したせん。 同じこずは移行にも圓おはたりたす。フィヌルドがないために移行されたせん。おそらく远加するだけです。 2番目のケヌスでは 、 Rubyが遅延しおいるずいう事実により、ブロックはモデルがアクセスされ、移行が正垞に実行されるずきにのみ実行されたす。



Has_many through



別の䞀般的な患者は



 product.documents.collect(&:lines).flatten
      
      





ここでは、補品には倚くの行がある倚くのドキュメントがありたす。 補品に関連するすべおのドキュメントのすべおの行を取埗したいこずがよくありたす。 そしお、この堎合、䞊蚘の構造を䜜成したす。 この堎合、リレヌションのスルヌオプションを呌び出しお、補品に察しお以䞋を実行できたす。



 has_many :lines, through: documents
      
      





そしお実行する



 product.lines
      
      





より明確か぀効率的になりたす。



JOINに぀いお少し



結合のトピックの続きで、 むンクルヌドに぀いお思い出しおください。 䜕がそんなに特別なのですか はい、それはLEFT JOINです。 かなり頻繁に、巊/右結合が明瀺的に蚘述されおいるこずがわかりたす



 joins("LEFT OUTER JOIN wikis ON wiki_pages.wiki_id=wikis.id")
      
      





確かに機胜したすが、RoRの玔粋なSQLは垞に奜たれたせん。



たた、レゞから逞脱するこずなく、 結合ず倀を䞀緒に䜿甚する堎合の倀の違いを思い出す必芁がありたす。 usersテヌブルがあり、 補品などのさたざたな゚ンティティにauthor_idフィヌルドずauthorリレヌションがあり、その䞋にusersテヌブルがあるずしたす。



 has_one :author, class: 'User', foreign_key: 'author_id' #  ,   
      
      





このような堎合、次のコヌドは機胜したせん 。



 products.joins(:author).where(author: {id: 42})
      
      





なんで 結合では、リレヌショナルの名前が瀺されたす。これはjoinimであり、テヌブルに条件が課される堎所であり、



 where(users: {id: 42})
      
      





これは、結合で明瀺的に「AS author」を指定するこずで回避できたすが、これも玔粋なSQLです。



次に、異なる角床から結合を芋おください。 参加するものは䜕でも、すべおを開始したクラスのオブゞェクトになりたす。



 Product.joins(:documents, :files, :etc).first
      
      





この堎合、結合の数に関係なく補品を取埗したす。 圌らは結合されたテヌブルからフィヌルドを受け取りたいので、この振る舞いはいくらか動揺したす。 そしお、圌らは反察偎から同じ芁求をし始めたすドキュメントを受け取り、それらを補品に参加させ、他の゚ンティティず通信するための玔粋なSQLを曞き、䞀般に、正しい論理コヌドが最初に曞かれたずきに自転車を発明したす。 したがっお、私はたさにその根拠を思い出したす



 Product.joins(:documents, :files, :etc).where(...).pluck('documents.type')
      
      





ここでは、デヌタベヌスから目的のフィヌルドを持぀配列を取埗したす。 長所最小限の芁求; ARオブゞェクトは䜜成されたせん。 短所Rails 3では、 pluckは1぀のパラメヌタヌのみを受け入れたすが、ここでは



 pluck('documents.type', 'files.filename', 'files.path')
      
      





Rails 4でのみ実行できたす。



関係を構築する



ここで、関係の構築を扱うこずの怜蚎に移りたしょう。 䞀般に、すべおは非垞に簡単です。



 product.documencts.build(type: 'article', etc: 'etc').lines.build(content: '...')
      
      





product.saveを呌び出した埌、すべおの関連付けが怜蚌、蚭定、遊女ずずもに保存されたす。 このすべおの楜しいアクションには1぀の泚意点がありたす。これは、 補品が 読み取り専甚でない堎合、および/たたは他のストレヌゞ制限がない堎合にすべお良奜です。 そのような堎合、倚くの人は、䞊蚘の䟋の結合を持぀庭に䌌た庭を配眮したす。 ぀たり、 ドキュメントを䜜成し、それを補品にバむンドしお、ドキュメントの行を䜜成したす。 デフォルトの動䜜は曲がっおおり、通垞ぱラヌ凊理に関係しおいるこずがわかりたすが、 補品は動䜜したせん。 したがっお、付属資料では、これはすべお束葉杖ですぐに囲たれ、間違いを投げお、かなり厄介なものになりたす。 この堎合の察凊方法 自動保存に぀いお芚えお、それがどのように機胜するかを理解する必芁がありたす。 詳现に入るこずなく、圌はコヌルバックに取り組んでいるず蚀いたす 。 したがっお、䞊蚘の補品の関係を保持する方法がありたす。



 product.autosave_associated_records_for_documents
      
      





この堎合、ドキュメントが保存され、そのコヌルバックが呌び出されお行が保存されたす。



むンデックスに぀いお䞀蚀



最埌に、倚くの人はむンデックスに基づいた問題のために固䜓オブゞェクトに頭をぶ぀けたため、むンデックスに぀いお蚀う必芁がありたす。 ActiveRecordずデヌタベヌスの機胜の倚くを劚害しおすぐに謝眪したすが、個人的な意芋ずしおは、デヌタベヌス偎でその瞬間に䜕が起こっおいるのかを理解しないずARをうたく動䜜させるこずはできたせん。



問題1



䜕らかの理由で、倚くの人は、 リレヌションの 順序が゜ヌトする列に䟝存しないこずを確信しおいたす。 この誀解のバリ゚ヌションは、 順序関係ず順序配列の違いを理解しおいないこずです。 このため、VARCHARフィヌルドの順序ず粟神の質問でdefault_scopeを満たすこずができたす。「ペヌゞの読み蟌みが遅いのはなぜですか そこでは、デヌタベヌスから取埗されるレコヌドはわずかです” ここでの問題は、この列にむンデックスがない堎合、デフォルトの゜ヌトが非垞に高䟡になるこずです。 デフォルトでは、ARはpkで゜ヌトしたす。 私たちがやるず起こる



 Products.first
      
      





ただし、 pkにはほが垞にむンデックスがあり、問題はありたせん。 しかし、モデルぞの呌び出しで順序名前がどうなるかを蚀うず、問題が始たりたす。

参考 「指で」説明するず、むンデックス付きの列で䞊べ替える堎合、実際の䞊べ替えは行われず、デヌタベヌスに既に存圚し、デヌタはすぐに正しい順序で送信されたす。



第二の問題



耇合むンデックス。 誰もがそれらに぀いお知っおいるわけではなく、さらに小さな人々の茪がなぜ圌らが必芁なのかを知っおいたす。 ぀たり、耇合むンデックスは、2぀以䞊のデヌタベヌスフィヌルドに基づくむンデックスです。 どこで䟿利になりたすか それを䜿甚する2぀の䞀般的な堎所

倚態的な接続に぀いおは、 ここで説明したした 。 倚くの堎合、耇合むンデックスを䜜成するず䟿利です。 以䞋は、 off.manulaのわずかに曎新された䟋です 。



 class CreatePictures < ActiveRecord::Migration def change create_table :pictures do |t| t.string :name t.integer :imageable_id t.string :imageable_type t.timestamps end add_index :pictures, [:imageable_id, :imageable_type] #     end end
      
      





ここに、通垞のむンデックスず耇合むンデックスの違いに぀いおのいく぀かの蚀葉がありたす。 このトピックは別のハブに関するものなので、これ以䞊詳现には觊れたせん。 さらに、 すべおがすでに私の前に描かれおいたした。

次に、䞭間リンクテヌブルに぀いお説明したす。 よく知られおいるHBTM 。 ここで、堎合によっおは、 assembly_partsに耇合むンデックスを投皿するこずが適切です HBTMリンクを参照。 ただし、耇合むンデックスのフィヌルドのシヌケンスには知識があるこずを芚えおおく必芁がありたす。 詳现はこちら 。



問題3



「むンデックスはどこでも必芁です」 それはそれほど䞀般的ではありたせんが、すべおの人にひどいブレヌキをかけたす。 むンデックスは䞇胜薬ではなく、x10-x100の速床を保蚌するものではなく、適切な堎所で䜿甚し、頭の䞊に振っお各穎に抌し蟌む必芁のないツヌルであるこずを芚えおおく必芁がありたす。 ここで、むンデックスの皮類に぀いお読むこずができ、 ここでむンデックスが必芁な理由を芋぀けるこずができたす。



シムすべおのために



最埌たで読んでくれおありがずう。 入力ミスや䞍正確な点に぀いおは、PMにご連絡ください。修正させおいただきたす。 たた、開発䞭にさたざたな状況で芚えおおくべきこずや䜿甚する方が良いこずに぀いお、「痛みを䌎う」経隓や経隓を共有しおいただければ幞いです。



All Articles