Ini lebih dari pertanyaan "mengapa hal-hal bekerja seperti ini" daripada pertanyaan "Saya tidak tahu bagaimana melakukan ini" ...
Jadi, Injil menarik catatan terkait yang Anda tahu akan Anda gunakan adalah menggunakan :include
karena Anda akan bergabung dan menghindari sejumlah pertanyaan tambahan:
Post.all(:include => :comments)
Namun ketika Anda melihat log, tidak ada gabungan yang terjadi:
Post Load (3.7ms) SELECT * FROM "posts"
Comment Load (0.2ms) SELECT "comments.*" FROM "comments"
WHERE ("comments".post_id IN (1,2,3,4))
ORDER BY created_at asc)
Hal ini mengambil jalan pintas karena menarik semua komentar sekaligus, tapi masih belum join (yang adalah apa yang semua dokumentasi sepertinya mengatakan). Satu-satunya cara saya dapat bergabung adalah dengan menggunakan :joins
alih-alih :include
:
Post.all(:joins => :comments)
Dan log menunjukkan:
Post Load (6.0ms) SELECT "posts".* FROM "posts"
INNER JOIN "comments" ON "posts".id = "comments".post_id
Apakah saya melewatkan sesuatu? Saya memiliki aplikasi dengan setengah lusin asosiasi dan pada satu layar saya menampilkan data dari semuanya. Sepertinya akan lebih baik untuk memiliki satu permintaan join-ed daripada 6 orang. Saya tahu bahwa menurut kinerja, tidak selalu lebih baik untuk melakukan penggabungan daripada kueri individual (bahkan jika Anda menghabiskan waktu, sepertinya dua kueri individual di atas lebih cepat daripada gabung), tetapi setelah semua dokumen Saya telah membaca saya terkejut melihat :include
tidak berfungsi seperti yang diiklankan.
Mungkin Rails adalah sadar akan masalah kinerja dan tidak bergabung kecuali dalam kasus-kasus tertentu?
sumber
includes
(untuk siapa pun yang membaca ini)Jawaban:
Tampaknya
:include
fungsionalitas diubah dengan Rails 2.1. Rails digunakan untuk melakukan join dalam semua kasus, tetapi untuk alasan kinerja itu diubah untuk menggunakan beberapa kueri dalam beberapa keadaan. Posting blog ini oleh Fabio Akita memiliki beberapa informasi bagus tentang perubahan tersebut (lihat bagian yang berjudul "Dioptimalkan Eager Memuat").sumber
.joins
hanya akan bergabung dengan tabel dan membawa bidang yang dipilih sebagai imbalan. jika Anda memanggil asosiasi pada hasil permintaan bergabung, itu akan memunculkan query database lagi:includes
akan dengan bersemangat memuat asosiasi yang disertakan dan menambahkannya dalam memori.:includes
memuat semua atribut tabel yang disertakan. Jika Anda memanggil asosiasi di hasil sertakan kueri, itu tidak akan memunculkan pertanyaansumber
Perbedaan antara gabungan dan sertakan adalah bahwa dengan menggunakan pernyataan sertakan menghasilkan kueri SQL yang jauh lebih besar memuat ke dalam semua atribut dari tabel lainnya.
Misalnya, jika Anda memiliki tabel yang penuh dengan komentar dan Anda menggunakan: joins => pengguna untuk menarik semua informasi pengguna untuk keperluan penyortiran, dll. Itu akan berfungsi dengan baik dan membutuhkan waktu lebih sedikit dari: sertakan, tetapi katakan Anda ingin menampilkan komentar bersama dengan nama pengguna, email, dll. Untuk mendapatkan informasi menggunakan: bergabung, itu harus membuat pertanyaan SQL terpisah untuk setiap pengguna yang dijemput, sedangkan jika Anda menggunakan: sertakan informasi ini siap digunakan.
Contoh yang bagus:
http://railscasts.com/episodes/181-include-vs-joins
sumber
Saya baru saja membaca lebih lanjut tentang perbedaan antara
:joins
dan:includes
di rel. Berikut ini adalah penjelasan dari apa yang saya mengerti (dengan contoh :))Pertimbangkan skenario ini:
Pengguna memiliki banyak komentar dan komentar menjadi milik Pengguna.
Model Pengguna memiliki atribut berikut: Nama (string), Usia (integer). Model Komentar memiliki atribut berikut: Konten, user_id. Untuk komentar, user_id bisa menjadi nol.
Bergabung:
: joins melakukan gabungan internal antara dua tabel. Jadi
akan mengambil semua catatan di mana user_id (dari tabel komentar) sama dengan user.id (tabel pengguna). Jadi, jika Anda melakukannya
Anda akan mendapatkan array kosong seperti yang ditunjukkan.
Selain itu bergabung tidak memuat tabel yang bergabung dalam memori. Jadi, jika Anda melakukannya
Seperti yang Anda lihat,
comment_1.user.age
akan menjalankan kueri basis data lagi di latar belakang untuk mendapatkan hasilTermasuk:
: termasuk melakukan gabungan luar kiri antara dua tabel. Jadi
akan menghasilkan tabel bergabung dengan semua catatan dari tabel komentar. Jadi, jika Anda melakukannya
itu akan mengambil catatan di mana comments.user_id tidak ada seperti yang ditunjukkan.
Apalagi termasuk memuat kedua tabel dalam memori. Jadi, jika Anda melakukannya
Karena Anda dapat melihat comment_1.user.age cukup memuat hasil dari memori tanpa melepaskan kueri basis data di latar belakang.
sumber
Selain pertimbangan kinerja, ada perbedaan fungsional juga. Ketika Anda bergabung dengan komentar, Anda meminta posting yang memiliki komentar - gabungan internal secara default. Ketika Anda memasukkan komentar, Anda meminta semua posting - gabungan luar.
sumber
tl; dr
Saya membandingkannya dengan dua cara:
joins - Untuk pemilihan rekaman bersyarat.
termasuk - Saat menggunakan asosiasi pada setiap anggota set hasil.
Versi yang lebih panjang
Bergabung dimaksudkan untuk memfilter set hasil yang berasal dari database. Anda menggunakannya untuk melakukan operasi yang ditetapkan di meja Anda. Pikirkan ini sebagai klausa mana yang melakukan teori himpunan.
Post.joins(:comments)
sama dengan
Post.where('id in (select post_id from comments)')
Kecuali bahwa jika ada lebih dari satu komentar, Anda akan mendapatkan duplikat kembali dengan bergabung. Tetapi setiap posting akan menjadi posting yang memiliki komentar. Anda dapat memperbaikinya dengan berbeda:
Dalam kontrak,
includes
metode ini hanya akan memastikan bahwa tidak ada kueri basis data tambahan saat mereferensikan relasi (sehingga kami tidak membuat n + 1 kueri)Moralnya adalah, gunakan
joins
ketika Anda ingin melakukan operasi pengaturan bersyarat dan gunakanincludes
ketika Anda akan menggunakan relasi pada setiap anggota koleksi.sumber
distinct
membuat saya setiap waktu. Terima kasih!.gabung berfungsi sebagai basis data bergabung dan itu menggabungkan dua atau lebih tabel dan mengambil data yang dipilih dari backend (database).
.termasuk kerja sebagai gabung kiri dari basis data. Itu memuat semua catatan sisi kiri, tidak memiliki relevansi model sisi kanan. Ini digunakan untuk mempercepat pemuatan karena memuat semua objek terkait dalam memori. Jika kita memanggil asosiasi pada hasil memasukkan kueri maka itu tidak memecat kueri pada database, itu hanya mengembalikan data dari memori karena sudah memuat data dalam memori.
sumber
'Bergabung' hanya digunakan untuk bergabung dengan tabel dan ketika Anda memanggil asosiasi bergabung, maka itu akan lagi memunculkan kueri (artinya banyak kueri akan memecat)
jumlah total SQL adalah 11 dalam hal ini
Tetapi dengan 'termasuk' akan bersemangat memuat asosiasi yang disertakan dan menambahkannya dalam memori (memuat semua asosiasi pada beban pertama) dan tidak mem-query lagi
ketika Anda mendapatkan catatan dengan menyertakan like @ records = User.includes (: organization) .where ("organisations.user_id = 1") maka kueri akan menjadi
@ records.map {| u | u.organisation.name} tidak ada permintaan yang akan diaktifkan
sumber