Dalam Django doc,
select_related()
"mengikuti" hubungan kunci-asing, memilih data objek terkait tambahan ketika mengeksekusi kueri.
prefetch_related()
melakukan pencarian terpisah untuk setiap hubungan, dan apakah "bergabung" dengan Python.
Apa yang dimaksud dengan "melakukan penggabungan dengan python"? Dapatkah seseorang mengilustrasikan dengan sebuah contoh?
Pemahaman saya adalah bahwa untuk hubungan kunci asing, gunakan select_related
; dan untuk hubungan M2M, gunakan prefetch_related
. Apakah ini benar?
python
django
django-models
django-orm
NeoWang
sumber
sumber
Jawaban:
Pemahaman Anda sebagian besar benar. Anda menggunakan
select_related
ketika objek yang akan Anda pilih adalah objek tunggal, jadiOneToOneField
atau aForeignKey
. Anda menggunakanprefetch_related
ketika Anda akan mendapatkan "set" hal-hal, begitu jugaManyToManyField
seperti yang Anda katakan atau membalikkanForeignKey
. Hanya untuk memperjelas apa yang saya maksud dengan "membalikkanForeignKey
" berikut ini sebuah contoh:Perbedaannya adalah
select_related
apakah SQL bergabung dan karenanya mendapatkan hasilnya kembali sebagai bagian dari tabel dari server SQL.prefetch_related
di sisi lain mengeksekusi kueri lain dan karenanya mengurangi kolom redundan pada objek asli (ModelA
dalam contoh di atas). Anda dapat menggunakanprefetch_related
untuk apa pun yang dapat Anda gunakanselect_related
untuk.Pengorbanan adalah yang
prefetch_related
harus membuat dan mengirim daftar ID untuk memilih kembali ke server, ini bisa memakan waktu cukup lama. Saya tidak yakin apakah ada cara yang baik untuk melakukan ini dalam transaksi, tetapi pemahaman saya adalah bahwa Django selalu hanya mengirim daftar dan mengatakan SELECT ... WHERE pk IN (..., ..., ...) pada dasarnya Dalam hal ini jika data yang diambil sebelumnya jarang (misalkan objek Negara AS yang ditautkan ke alamat orang) ini bisa sangat baik, namun jika lebih dekat dengan satu-ke-satu, ini dapat membuang banyak komunikasi. Jika ragu, coba keduanya dan lihat mana yang berkinerja lebih baik.Segala sesuatu yang dibahas di atas pada dasarnya adalah tentang komunikasi dengan database. Namun di sisi Python
prefetch_related
memiliki manfaat tambahan bahwa satu objek digunakan untuk mewakili setiap objek dalam database. Denganselect_related
duplikat objek akan dibuat dengan Python untuk setiap objek "induk". Karena objek dalam Python memiliki sedikit memori yang layak, ini juga dapat menjadi pertimbangan.sumber
select_related
adalah satu permintaan sementaraprefetch_related
dua, jadi yang pertama lebih cepat. Tetapiselect_related
tidak akan membantu Anda untukManyToManyField
'sselect_related
menggunakan BERGABUNG dalam SQL saatprefetch_related
menjalankan kueri pada model pertama, mengumpulkan semua ID yang perlu prefetch dan kemudian menjalankan kueri dengan klausa IN di WHERE dengan semua ID yang dibutuhkan. Jika Anda mengatakan 3-5 model menggunakan kunci asing yang sama,select_related
hampir pasti akan lebih baik. Jika Anda memiliki 100 atau 1000 model yang menggunakan kunci asing yang sama,prefetch_related
sebenarnya bisa lebih baik. Di antara Anda harus menguji dan melihat apa yang terjadi.Kedua metode mencapai tujuan yang sama, untuk melepaskan pertanyaan db yang tidak perlu. Tetapi mereka menggunakan pendekatan berbeda untuk efisiensi.
Satu-satunya alasan untuk menggunakan salah satu metode ini adalah ketika satu kueri besar lebih disukai daripada banyak kueri kecil. Django menggunakan permintaan besar untuk membuat model dalam memori lebih dulu daripada melakukan permintaan permintaan terhadap database.
select_related
melakukan gabungan dengan setiap pencarian, tetapi memperluas pemilihan untuk menyertakan kolom dari semua tabel yang bergabung. Namun pendekatan ini memiliki peringatan.Bergabung memiliki potensi untuk mengalikan jumlah baris dalam kueri. Saat Anda melakukan penggabungan atas kunci asing atau bidang satu-ke-satu, jumlah baris tidak akan bertambah. Namun, banyak-ke-banyak bergabung tidak memiliki jaminan ini. Jadi, Django membatasi
select_related
hubungan yang tidak terduga menghasilkan gabungan besar.The "bergabung dalam python" untuk
prefetch_related
sedikit lebih mengkhawatirkan maka harus. Ini membuat kueri terpisah untuk setiap tabel yang akan digabung. Ini memfilter masing-masing tabel ini dengan klausa WHERE IN, seperti:Alih-alih melakukan gabungan tunggal dengan berpotensi terlalu banyak baris, setiap tabel dibagi menjadi kueri yang terpisah.
sumber
Seperti yang dikatakan dokumentasi Django:
Informasi lebih lanjut tentang ini: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#prefetch-related
sumber
Pergi melalui jawaban yang sudah diposting. Hanya berpikir akan lebih baik jika saya menambahkan jawaban dengan contoh aktual.
Katakanlah Anda memiliki 3 model Django yang terkait.
Di sini Anda dapat meminta
M2
model danM1
objek relatifnya menggunakanselect_relation
bidang danM3
objek menggunakanprefetch_relation
bidang.Namun seperti yang telah kami sebutkan
M1
relasinya dariM2
aForeignKey
, hanya mengembalikan 1 record untukM2
objek apa pun . Hal yang sama berlakuOneToOneField
juga.Tapi
M3
hubungan dariM2
adalahManyToManyField
yang dapat mengembalikan sejumlahM1
objek.Pertimbangkan kasus di mana Anda memiliki 2
M2
objekm21
,m22
yang memiliki 5M3
objek terkait yang sama dengan ID1,2,3,4,5
. Ketika Anda mengambilM3
objek terkait untuk masing-masingM2
objek tersebut, jika Anda menggunakan pilih terkait, ini adalah cara kerjanya.Langkah:
m21
objek.M3
objek yang terkait denganm21
objek yang memiliki ID1,2,3,4,5
.m22
objek dan semuaM2
objek lainnya .Karena kami memiliki
1,2,3,4,5
ID yang sama untuk keduanyam21
,m22
objek, jika kami menggunakan opsi select_related, itu akan meminta DB dua kali untuk ID yang sama yang sudah diambil.Alih-alih jika Anda menggunakan prefetch_related, ketika Anda mencoba untuk mendapatkan
M2
objek, itu akan membuat catatan dari semua ID yang dikembalikan objek Anda (Catatan: hanya ID) saat melakukan kueriM2
tabel dan sebagai langkah terakhir, Django akan membuat kueri keM3
tabel dengan set semua ID yangM2
objek Anda telah kembali. dan bergabung dengan mereka keM2
objek menggunakan Python, bukan database.Dengan cara ini Anda hanya menanyakan satu
M3
objek sekali saja yang meningkatkan kinerja.sumber