擁有 Author 和 Book 模型,其中 Author has_many 書籍,以及 Author 的范圍,例如:
scope :by_books,->(book_ids) { joins(:books).where(books: { id: book_ids }) }
如果作者是給定書籍的作者,則旨在過濾作者,僅在以下結果中產生作者和指定書籍:
Author.by_books(1).includes(:books).as_json(include: :books)
而我希望在生成的 json 中包含作者的所有書籍。我可以通過使用.joins(:books)而不是來做到這一點,.includes(:books)但現在我對 join 和 include 的作用完全感到困惑。我想知道是否有人可以解釋我的期望出錯的地方。
(給出的代碼與我的實際代碼類似,它可能在語法方面存在缺陷,但我相信行為是相同的)
uj5u.com熱心網友回復:
joins是連接 2 個表的純 SQL 單詞查詢。includes用于急切加載(避免n 1查詢問題)
??
uj5u.com熱心網友回復:
class Author < ApplicationRecord
has_many :books
end
class Book < ApplicationRecord
belongs_to :author
end
Author.create!([
{ name: 'Author 1',
books: [ Book.new(name: 'Book 1 of A1'), Book.new(name: 'Book 2 of A1') ] },
{ name: 'Author 2',
books: [ Book.new(name: 'Book 1 of A2') ] },
{ name: 'Author 3' }
])
您必須小心joinsand includes,它們會根據關聯回傳不同的結果。
我們有“作者 1”有兩本書,“作者 2”有一本書,“作者 3”沒有書。
joins執行 INNER JOIN 并根據資料庫結果實體化新物件,這可能會回傳重復記錄而不回傳沒有關聯的記錄
>> Author.joins(:books)
Author Load (1.3ms) SELECT "authors".* FROM "authors" INNER JOIN "books" ON "books"."author_id" = "authors"."id"
=> [ #<Author:0x00007f0dd278d818 id: 1, name: "Author 1">,
#<Author:0x00007f0dd278d688 id: 1, name: "Author 1">, # duplicate
#<Author:0x00007f0dd278d598 id: 2, name: "Author 2">]
# 'Author 3' is not in the result
includes是一個軌道解決方案N 1。它運行兩個查詢akapreload或單個查詢akaeager_load
>> Author.includes(:books)
Author Load (0.8ms) SELECT "authors".* FROM "authors"
Book Load (1.1ms) SELECT "books".* FROM "books" WHERE "books"."author_id" IN ($1, $2, $3) [["author_id", 1], ["author_id", 2], ["author_id", 3]]
=> [ #<Author:0x00007f0dd27e59c8 id: 1, name: "Author 1">,
#<Author:0x00007f0dd27e5900 id: 2, name: "Author 2">,
#<Author:0x00007f0dd27e5838 id: 3, name: "Author 3">]
當您有條件時,它會eager_load在單個查詢中執行
# same as Author.eager_load(:books)
>> Author.includes(:books).references(:books)
SQL (1.0ms) SELECT "authors"."id" AS t0_r0, "authors"."name" AS t0_r1, "books"."id" AS t1_r0, "books"."name" AS t1_r1, "books"."author_id" AS t1_r2 FROM "authors" LEFT OUTER JOIN "books" ON "books"."author_id" = "authors"."id"
=> [ #<Author:0x00007f0dd283a1f8 id: 1, name: "Author 1">,
#<Author:0x00007f0dd2839bb8 id: 2, name: "Author 2">,
#<Author:0x00007f0dd2839780 id: 3, name: "Author 3">]
有了這個,您可以獲得所有有或沒有書籍的作者,書籍物件被預加載到作者物件中。
>> Author.includes(:books).to_a.first.instance_variable_get('@association_cache')
Author Load (0.7ms) SELECT "authors".* FROM "authors"
Book Load (0.7ms) SELECT "books".* FROM "books" WHERE "books"."author_id" IN ($1, $2, $3) [["author_id", 1], ["author_id", 2], ["author_id", 3]]
=> {:books=>
#<ActiveRecord::Associations::HasManyAssociation:0x00007f0dd28ff9d0
@association_ids=nil,
@association_scope=nil,
@disable_joins=false,
@loaded=true,
@owner=#<Author:0x00007f0dd28f83b0 id: 1, name: "Author 1">,
@reflection=#<ActiveRecord::Reflection::HasManyReflection:0x00007f0dd58a3f38 ... >,
@replaced_or_added_targets=#<Set: {}>,
@stale_state=nil,
@target=[ #<Book:0x00007f0dd28fc230 id: 1, name: "Book 1 of A1", author_id: 1>,
#<Book:0x00007f0dd28fc078 id: 2, name: "Book 2 of A1", author_id: 1>]>}
# ^
# books are preloaded
>> Author.includes(:books).to_a.last.instance_variable_get('@association_cache')
Author Load (0.9ms) SELECT "authors".* FROM "authors"
Book Load (0.7ms) SELECT "books".* FROM "books" WHERE "books"."author_id" IN ($1, $2, $3) [["author_id", 1], ["author_id", 2], ["author_id", 3]]
=> {:books=>
#<ActiveRecord::Associations::HasManyAssociation:0x00007f0dd2a017c0
...,
@target=[]>}
# ^
# doesn't have any books
當包含時,eager_load它會運行 LEFT OUTER JOIN,因此您可以獲得所有作者,并且 rails 組合了重復的結果,這與joins方法不同。
>> ActiveRecord::Base.connection.execute(Author.eager_load(:books).to_sql).to_a
(0.8ms) SELECT "authors"."id" AS t0_r0, "authors"."name" AS t0_r1, "books"."id" AS t1_r0, "books"."name" AS t1_r1, "books"."author_id" AS t1_r2 FROM "authors" LEFT OUTER JOIN "books" ON "books"."author_id" = "authors"."id"
=>
[{"t0_r0"=>1, "t0_r1"=>"Author 1", "t1_r0"=>1, "t1_r1"=>"Book 1 of A1", "t1_r2"=>1},
{"t0_r0"=>1, "t0_r1"=>"Author 1", "t1_r0"=>2, "t1_r1"=>"Book 2 of A1", "t1_r2"=>1},
# ^ duplicate `Author 1` like the `joins` method
{"t0_r0"=>2, "t0_r1"=>"Author 2", "t1_r0"=>3, "t1_r1"=>"Book 1 of A2", "t1_r2"=>2},
{"t0_r0"=>3, "t0_r1"=>"Author 3", "t1_r0"=>nil, "t1_r1"=>nil, "t1_r2"=>nil}]
“作者 1”結果被合并到一個Author物件中。
您正在運行joins,includes同時結合執行eager_load樣式查詢。
Author.joins(:books).includes(:books).where(books: {id: [1,2]})
# INNER JOIN
# 2 database results
# 1 Author object
# Books are preloaded
Author.joins(:books).where(books: {id: [1,2]})
# INNER JOIN
# 2 database results
# 2 Author objects
# No books are loaded
Author.includes(:books).where(books: {id: [1,2]})
# LEFT OUTER JOIN
# 2 database results
# 1 Author object
# Books are preloaded
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/464551.html
