Saya telah membuat perintah SQL yang menggunakan INNER JOIN pada 9 tabel, toh perintah ini membutuhkan waktu yang sangat lama (lebih dari lima menit). Jadi rakyat saya menyarankan saya untuk mengubah INNER JOIN menjadi LEFT JOIN karena kinerja LEFT JOIN lebih baik, terlepas dari apa yang saya ketahui. Setelah saya mengubahnya, kecepatan kueri membaik secara signifikan.
Saya ingin tahu mengapa LEFT JOIN lebih cepat daripada INNER JOIN?
Perintah SQL saya terlihat seperti di bawah ini:
SELECT * FROM A INNER JOIN B ON ... INNER JOIN C ON ... INNER JOIN D
dan seterusnya
Pembaruan: Ini adalah ringkasan skema saya.
FROM sidisaleshdrmly a -- NOT HAVE PK AND FK
INNER JOIN sidisalesdetmly b -- THIS TABLE ALSO HAVE NO PK AND FK
ON a.CompanyCd = b.CompanyCd
AND a.SPRNo = b.SPRNo
AND a.SuffixNo = b.SuffixNo
AND a.dnno = b.dnno
INNER JOIN exFSlipDet h -- PK = CompanyCd, FSlipNo, FSlipSuffix, FSlipLine
ON a.CompanyCd = h.CompanyCd
AND a.sprno = h.AcctSPRNo
INNER JOIN exFSlipHdr c -- PK = CompanyCd, FSlipNo, FSlipSuffix
ON c.CompanyCd = h.CompanyCd
AND c.FSlipNo = h.FSlipNo
AND c.FSlipSuffix = h.FSlipSuffix
INNER JOIN coMappingExpParty d -- NO PK AND FK
ON c.CompanyCd = d.CompanyCd
AND c.CountryCd = d.CountryCd
INNER JOIN coProduct e -- PK = CompanyCd, ProductSalesCd
ON b.CompanyCd = e.CompanyCd
AND b.ProductSalesCd = e.ProductSalesCd
LEFT JOIN coUOM i -- PK = UOMId
ON h.UOMId = i.UOMId
INNER JOIN coProductOldInformation j -- PK = CompanyCd, BFStatus, SpecCd
ON a.CompanyCd = j.CompanyCd
AND b.BFStatus = j.BFStatus
AND b.ProductSalesCd = j.ProductSalesCd
INNER JOIN coProductGroup1 g1 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup1Cd
ON e.ProductGroup1Cd = g1.ProductGroup1Cd
INNER JOIN coProductGroup2 g2 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup2Cd
ON e.ProductGroup1Cd = g2.ProductGroup1Cd
sql
sql-server
performance
Anonim
sumber
sumber
coUOM
? Jika tidak, Anda mungkin dapat menggunakan semi gabung. Jika ya, Anda bisa menggunakanUNION
sebagai alternatif. Memposting hanyaFROM
klausa Anda tidak cukup informasi di sini.Jawaban:
A
LEFT JOIN
sama sekali tidak lebih cepat dariINNER JOIN
. Bahkan, ini lebih lambat; menurut definisi, gabungan luar (LEFT JOIN
atauRIGHT JOIN
) harus melakukan semua pekerjaanINNER JOIN
ditambah pekerjaan tambahan dari null-memperluas hasil. Itu juga akan diharapkan untuk mengembalikan lebih banyak baris, lebih lanjut meningkatkan total waktu eksekusi hanya karena ukuran yang lebih besar dari hasil yang ditetapkan.(Dan bahkan jika
LEFT JOIN
itu lebih cepat dalam spesifik situasi karena beberapa sulit-untuk-membayangkan pertemuan faktor, tidak fungsional setara denganINNER JOIN
, sehingga Anda tidak bisa hanya pergi mengganti semua contoh dari satu dengan yang lain!)Kemungkinan besar masalah kinerja Anda terletak di tempat lain, seperti tidak memiliki kunci kandidat atau kunci asing diindeks dengan benar. 9 tabel cukup banyak untuk bergabung sehingga perlambatan bisa benar-benar hampir di mana saja. Jika Anda memposting skema Anda, kami mungkin dapat memberikan detail lebih lanjut.
Edit:
Merenungkan lebih lanjut tentang ini, saya bisa memikirkan satu keadaan di mana a
LEFT JOIN
mungkin lebih cepat daripadaINNER JOIN
, dan saat itulah:Pertimbangkan contoh ini:
Jika Anda menjalankan ini dan melihat rencana eksekusi, Anda akan melihat bahwa
INNER JOIN
permintaan memang lebih mahal daripadaLEFT JOIN
, karena memenuhi dua kriteria di atas. Itu karena SQL Server ingin melakukan hash cocok untukINNER JOIN
, tetapi tidak bersarang loop untukLEFT JOIN
; yang pertama biasanya jauh lebih cepat, tetapi karena jumlah baris sangat kecil dan tidak ada indeks untuk digunakan, operasi hashing ternyata menjadi bagian paling mahal dari permintaan.Anda dapat melihat efek yang sama dengan menulis program dalam bahasa pemrograman favorit Anda untuk melakukan banyak pencarian pada daftar dengan 5 elemen, vs. tabel hash dengan 5 elemen. Karena ukurannya, versi tabel hash sebenarnya lebih lambat. Tetapi tingkatkan menjadi 50 elemen, atau 5000 elemen, dan versi daftar melambat menjadi perayapan, karena itu O (N) vs O (1) untuk hashtable.
Tetapi ubah kueri ini menjadi pada
ID
kolom alih-alihName
dan Anda akan melihat cerita yang sangat berbeda. Dalam hal ini, ia melakukan loop bersarang untuk kedua kueri, tetapiINNER JOIN
versi ini dapat menggantikan salah satu pemindaian indeks berkerumun dengan sebuah pencarian - yang berarti bahwa ini akan benar-benar menjadi urutan besarnya lebih cepat dengan sejumlah besar baris.Jadi kesimpulannya kurang lebih seperti yang saya sebutkan beberapa paragraf di atas; ini hampir pasti masalah pengindeksan atau cakupan indeks, mungkin dikombinasikan dengan satu atau lebih tabel yang sangat kecil. Itulah satu-satunya keadaan di mana SQL Server kadang-kadang mungkin memilih rencana eksekusi yang lebih buruk untuk
INNER JOIN
daripadaLEFT JOIN
.sumber
Ada satu skenario penting yang dapat menyebabkan sambungan luar menjadi lebih cepat daripada sambungan batin yang belum dibahas.
Saat menggunakan gabungan luar, optimizer selalu bebas untuk menjatuhkan tabel gabungan luar dari rencana eksekusi jika kolom gabungan adalah PK dari tabel luar, dan tidak ada kolom tabel luar yang dirujuk di luar gabungan luar itu sendiri. Sebagai contoh
SELECT A.* FROM A LEFT OUTER JOIN B ON A.KEY=B.KEY
dan B.KEY adalah PK untuk B. Baik Oracle (saya percaya saya menggunakan rilis 10) dan Sql Server (saya menggunakan 2008 R2) memangkas tabel B dari rencana eksekusi.Hal yang sama tidak selalu benar untuk gabungan dalam:
SELECT A.* FROM A INNER JOIN B ON A.KEY=B.KEY
mungkin atau mungkin tidak memerlukan B dalam rencana eksekusi tergantung pada kendala apa yang ada.Jika A.KEY adalah kunci asing nullable yang mereferensikan B.KEY, maka pengoptimal tidak dapat menjatuhkan B dari rencana karena harus mengkonfirmasi bahwa ada baris B untuk setiap baris A.
Jika A.KEY adalah kunci asing kunci yang mereferensikan B.KEY, maka pengoptimal bebas untuk menjatuhkan B dari rencana karena kendala menjamin keberadaan baris. Tetapi hanya karena pengoptimal dapat menjatuhkan tabel dari rencana, tidak berarti itu akan berhasil. SQL Server 2008 R2 TIDAK menjatuhkan B dari paket. Oracle 10 TIDAK menjatuhkan B dari rencana. Sangat mudah untuk melihat bagaimana bergabung luar akan melakukan bergabung dalam SQL Server dalam kasus ini.
Ini adalah contoh sepele, dan tidak praktis untuk kueri yang berdiri sendiri. Mengapa bergabung dengan sebuah meja jika Anda tidak perlu?
Tapi ini bisa menjadi pertimbangan desain yang sangat penting saat mendesain tampilan. Sering kali tampilan "do-everything" dibangun yang menggabungkan semua yang mungkin perlu pengguna terkait dengan tabel pusat. (Terutama jika ada pengguna naif yang melakukan kueri ad-hoc yang tidak memahami model relasional) Tampilan dapat mencakup semua kolom relevan dari banyak tabel. Tetapi pengguna akhir hanya dapat mengakses kolom dari subset dari tabel dalam tampilan. Jika tabel digabungkan dengan gabungan luar, maka pengoptimal dapat (dan memang) menjatuhkan tabel yang tidak diperlukan dari rencana.
Sangat penting untuk memastikan bahwa tampilan menggunakan gabungan luar memberikan hasil yang benar. Seperti yang Aaronaught katakan - Anda tidak bisa secara buta mengganti OUTER JOIN dengan INNER JOIN dan mengharapkan hasil yang sama. Tetapi ada kalanya hal itu berguna untuk alasan kinerja saat menggunakan tampilan.
Satu catatan terakhir - Saya belum menguji dampak pada kinerja di atas, tetapi secara teori tampaknya Anda harus dapat dengan aman mengganti INNER JOIN dengan OUTER JOIN jika Anda juga menambahkan kondisi <FOREIGN_KEY> TIDAK NULL ke klausa mana.
sumber
Jika semuanya berfungsi sebagaimana mestinya tidak seharusnya, TETAPI kita semua tahu segalanya tidak berjalan seperti seharusnya terutama ketika menyangkut pengoptimal kueri, caching rencana kueri, dan statistik.
Pertama saya akan menyarankan untuk membangun kembali indeks dan statistik, kemudian membersihkan cache rencana kueri hanya untuk memastikan itu tidak mengacaukan segalanya. Namun saya sudah mengalami masalah bahkan ketika itu selesai.
Saya pernah mengalami beberapa kasus di mana join kiri lebih cepat daripada join internal.
Alasan yang mendasarinya adalah ini: Jika Anda memiliki dua tabel dan Anda bergabung pada kolom dengan indeks (pada kedua tabel). Gabungan dalam akan menghasilkan hasil yang sama tidak masalah jika Anda mengulang entri dalam indeks pada tabel satu dan mencocokkan dengan indeks pada tabel dua seolah-olah Anda akan melakukan sebaliknya: Loop atas entri dalam indeks pada tabel dua dan cocok dengan indeks dalam tabel satu. Masalahnya adalah ketika Anda memiliki statistik yang menyesatkan, pengoptimal kueri akan menggunakan statistik indeks untuk menemukan tabel dengan entri yang paling tidak cocok (berdasarkan kriteria Anda yang lain). Jika Anda memiliki dua tabel dengan 1 juta di masing-masing, dalam tabel satu Anda memiliki 10 baris yang cocok dan di tabel dua Anda memiliki 100000 baris yang cocok. Cara terbaik adalah melakukan pemindaian indeks pada tabel satu dan mencocokkan 10 kali dalam tabel dua. Kebalikannya adalah pemindaian indeks yang loop lebih dari 100000 baris dan mencoba untuk mencocokkan 100000 kali dan hanya 10 berhasil. Jadi, jika statistik tidak benar, pengoptimal mungkin memilih tabel yang salah dan indeks untuk diulang.
Jika optimizer memilih untuk mengoptimalkan gabung kiri dalam urutan yang tertulis itu akan berkinerja lebih baik daripada gabung dalam.
NAMUN, pengoptimal juga dapat mengoptimalkan gabung kiri secara kurang optimal sebagai gabung semi kiri. Untuk membuatnya memilih yang Anda inginkan, Anda dapat menggunakan petunjuk urutan kekuatan.
sumber
Coba kedua pertanyaan (yang dengan bagian dalam dan kiri bergabung) dengan
OPTION (FORCE ORDER)
di akhir dan memposting hasilnya.OPTION (FORCE ORDER)
adalah petunjuk kueri yang memaksa optimizer untuk membangun rencana eksekusi dengan pesanan gabungan yang Anda berikan dalam kueri.Jika
INNER JOIN
mulai bekerja secepatLEFT JOIN
, itu karena:INNER JOIN
s, urutan bergabung tidak masalah. Ini memberikan kebebasan bagi optimizer kueri untuk memesan gabungan yang dianggapnya sesuai, sehingga masalahnya mungkin bergantung pada optimizer.LEFT JOIN
, bukan itu masalahnya karena mengubah urutan bergabung akan mengubah hasil kueri. Ini berarti mesin harus mengikuti urutan bergabung yang Anda berikan pada kueri, yang mungkin lebih baik daripada yang dioptimalkan.Tidak tahu apakah ini menjawab pertanyaan Anda, tetapi saya pernah berada di sebuah proyek yang menampilkan pertanyaan yang sangat rumit membuat perhitungan, yang benar-benar mengacaukan pengoptimal. Kami memiliki kasus di mana a
FORCE ORDER
akan mengurangi waktu eksekusi permintaan dari 5 menit menjadi 10 detik.sumber
Telah melakukan sejumlah perbandingan antara gabungan luar dan dalam kiri dan belum dapat menemukan perbedaan yang konsisten. Ada banyak variabel. Sedang mengerjakan database pelaporan dengan ribuan tabel banyak dengan banyak bidang, banyak perubahan seiring waktu (versi vendor dan alur kerja lokal). Tidak mungkin membuat semua kombinasi indeks penutup untuk memenuhi kebutuhan beragam pertanyaan dan menangani data historis. Telah melihat permintaan dalam yang mematikan kinerja server karena dua tabel besar (jutaan hingga puluhan juta baris) digabungkan untuk menarik sejumlah besar bidang dan tidak ada indeks penutup.
Namun masalah terbesar, tampaknya tidak cocok dalam diskusi di atas. Mungkin basis data Anda dirancang dengan baik dengan pemicu dan pemrosesan transaksi yang dirancang dengan baik untuk memastikan data yang baik. Milik saya sering memiliki nilai NULL yang tidak diharapkan. Ya, definisi tabel bisa memberlakukan no-Nulls tetapi itu bukan opsi di lingkungan saya.
Jadi pertanyaannya adalah ... apakah Anda mendesain kueri hanya untuk kecepatan, prioritas yang lebih tinggi untuk pemrosesan transaksi yang menjalankan kode yang sama ribuan kali dalam satu menit. Atau apakah Anda mencari keakuratan yang akan disediakan oleh sambungan luar kiri. Ingatlah bahwa gabungan internal harus menemukan kecocokan di kedua sisi, sehingga NULL yang tidak terduga tidak hanya akan menghapus data dari dua tabel tetapi juga seluruh baris informasi. Dan itu terjadi dengan sangat baik, tidak ada pesan kesalahan.
Anda bisa sangat cepat karena mendapatkan 90% dari data yang dibutuhkan dan tidak menemukan sambungan dalam telah menghapus informasi secara diam-diam. Kadang-kadang gabungan batin bisa lebih cepat, tetapi saya tidak percaya ada orang yang membuat asumsi itu kecuali mereka telah meninjau rencana eksekusi. Kecepatan itu penting, tetapi akurasi lebih penting.
sumber
Masalah kinerja Anda lebih cenderung karena jumlah gabungan yang Anda lakukan dan apakah kolom yang Anda ikuti memiliki indeks atau tidak.
Kasus terburuk Anda dapat dengan mudah melakukan 9 scan seluruh tabel untuk setiap bergabung.
sumber
Gabungan luar dapat menawarkan kinerja superior saat digunakan dalam tampilan.
Katakanlah Anda memiliki kueri yang melibatkan tampilan, dan tampilan itu terdiri dari 10 tabel yang digabungkan. Katakanlah permintaan Anda hanya terjadi menggunakan kolom dari 3 dari 10 tabel itu.
Jika 10 tabel tersebut telah digabungkan bersama, maka pengoptimal kueri harus menggabungkan semuanya meskipun kueri Anda sendiri tidak memerlukan 7 dari 10 tabel. Itu karena bagian dalam bergabung sendiri mungkin menyaring data, membuat mereka penting untuk dihitung.
Jika 10 tabel tersebut telah digabungkan bersama, maka pengoptimal kueri hanya akan benar-benar bergabung dengan yang diperlukan: 3 dari 10 di antaranya dalam kasus ini. Itu karena gabungan itu sendiri tidak lagi memfilter data, dan dengan demikian gabungan yang tidak terpakai dapat dilewati.
Sumber: http://www.sqlservercentral.com/blogs/sql_coach/2010/07/29/poor-little-misunderstood-views/
sumber
Saya menemukan sesuatu yang menarik di SQL server ketika memeriksa apakah gabungan lebih cepat dari gabungan kiri.
Jika Anda tidak menyertakan item dari tabel gabungan kiri, dalam pernyataan pilih, gabungan kiri akan lebih cepat daripada permintaan yang sama dengan gabungan dalam.
Jika Anda menyertakan tabel gabungan kiri dalam pernyataan pilih, gabungan dalam dengan kueri yang sama sama atau lebih cepat dari gabungan kiri.
sumber
Dari perbandingan saya, saya menemukan bahwa mereka memiliki rencana eksekusi yang sama persis. Ada tiga skenario:
Jika dan ketika mereka mengembalikan hasil yang sama, mereka memiliki kecepatan yang sama. Namun, kita harus ingat bahwa mereka bukan pertanyaan yang sama, dan LEFT JOIN mungkin akan memberikan lebih banyak hasil (ketika beberapa kondisi ON tidak terpenuhi) --- inilah mengapa biasanya lebih lambat.
Ketika tabel utama (non-const pertama dalam rencana eksekusi) memiliki kondisi terbatas (WHERE id =?) Dan kondisi ON terkait pada nilai NULL, tabel "kanan" tidak digabungkan --- ini adalah saat LEFT JOIN lebih cepat.
Seperti dibahas dalam Poin 1, biasanya INNER JOIN lebih membatasi dan mengembalikan hasil lebih sedikit dan karenanya lebih cepat.
Keduanya menggunakan indeks (sama).
sumber