Baru-baru ini, saya menemukan aplikasi yang menghasilkan kueri DB. Saya mengerti bahwa tidak ada yang baru tentang itu, tetapi ketika aplikasi mulai berjalan lambat dan saya harus mencari tahu alasan perlambatan, saya kagum menemukan pertanyaan-pertanyaan ini. Inilah yang terkadang harus dihadapi SQL Server:
SELECT COUNT(DISTINCT "pr"."id") FROM ((((((((((((((((("SomeTable" "pr" LEFT OUTER JOIN "SomeTable1698" "uf_pr_id_698" ON "uf_pr_id_698"."request" = "pr"."id") LEFT OUTER JOIN "SomeTable1700" "ufref3737_i2" ON "ufref3737_i2"."request" = "pr"."id") LEFT OUTER JOIN "SomeTable1666" "x0" ON "x0"."request" = "ufref3737_i2"."f6_callerper") LEFT OUTER JOIN "SomeTable1666" "uf_ufref4646_i3_f58__666" ON "uf_ufref4646_i3_f58__666"."request" = "ufref3737_i2"."f58_") LEFT OUTER JOIN "SomeTable1694" "x1" ON "x1"."request" = "ufref3737_i2"."f38_servicep") LEFT OUTER JOIN "SomeTable3754" "ufref3754_i12" ON "pr"."id" = "ufref3754_i12"."request") LEFT OUTER JOIN "SomeTable1698" "uf_ufref3754_i12_reference_698" ON "uf_ufref3754_i12_reference_698"."request" = "ufref3754_i12"."reference") LEFT OUTER JOIN "SomeTable1698" "x2" ON "x2"."request" = "ufref3737_i2"."f34_parentse") LEFT OUTER JOIN "SomeTable4128" "ufref3779_4128_i14" ON "ufref3737_i2"."f34_parentse" = "ufref3779_4128_i14"."request") LEFT OUTER JOIN "SomeTable1859" "x3" ON "x3"."request" = "ufref3779_4128_i14"."reference") LEFT OUTER JOIN "SomeTable3758" "ufref3758_i15" ON "pr"."id" = "ufref3758_i15"."request") LEFT OUTER JOIN "SomeTable1698" "uf_ufref3758_i15_reference_698" ON "uf_ufref3758_i15_reference_698"."request" = "ufref3758_i15"."reference") LEFT OUTER JOIN "SomeTable3758" "ufref3758_i16" ON "pr"."id" = "ufref3758_i16"."request") LEFT OUTER JOIN "SomeTable4128" "ufref3758_4128_i16" ON "ufref3758_i16"."reference" = "ufref3758_4128_i16"."request") LEFT OUTER JOIN "SomeTable1859" "x4" ON "x4"."request" = "ufref3758_4128_i16"."reference") LEFT OUTER JOIN "SomeTable4128" "ufref4128_i17" ON "pr"."id" = "ufref4128_i17"."request") LEFT OUTER JOIN "SomeTable1859" "uf_ufref4128_i17_reference_859" ON "uf_ufref4128_i17_reference_859"."request" = "ufref4128_i17"."reference") LEFT OUTER JOIN "SomeTable1666" "uf_ufref4667_i25_f69__666" ON "uf_ufref4667_i25_f69__666"."request" = "uf_pr_id_698"."f69_" WHERE ("uf_pr_id_698"."f1_applicant" IN (248,169,180,201,203,205,209,215,223,357,371,379,3502,3503,3506,3514,3517,3531,3740,3741) OR "x0"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "uf_ufref4646_i3_f58__666"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "uf_ufref4667_i25_f69__666"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR ("uf_pr_id_698"."f10_status" Is Null OR "uf_pr_id_698"."f10_status" <> 111) AND "ufref3737_i2"."f96_" = 0 AND (("ufref3737_i2"."f17_source" Is Null OR "ufref3737_i2"."f17_source" <> 566425) AND ("ufref3737_i2"."f17_source" Is Null OR "ufref3737_i2"."f17_source" <> 566424) OR ("uf_pr_id_698"."f10_status" Is Null OR "uf_pr_id_698"."f10_status" <> 56) ) AND ("uf_pr_id_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "x1"."f19_restrict" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "uf_ufref3754_i12_reference_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "x2"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "x3"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "uf_ufref3758_i15_reference_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "x4"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "uf_ufref4128_i17_reference_859"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)) AND ("uf_pr_id_698"."f12_responsi" Is Null OR "uf_pr_id_698"."f12_responsi" <> 579420) ) AND "pr"."area" IN (700) AND "pr"."area" IN (700) AND "pr"."deleted_by_user"=0 AND "pr"."temporary" = 0
Nama objek telah diubah.
Hal yang paling mencolok adalah bahwa tabel yang sama digunakan beberapa kali, dan jumlah tanda kurung membuat saya gila. Saya bukan satu-satunya yang tidak menyukai kode ini, SQL Server juga tidak menghargainya dan menghabiskan banyak sumber daya untuk membuat rencana untuk itu. Kueri dapat berjalan selama 50-150 md, dan pembuatan rencana dapat memakan waktu hingga 2,5 md. Hari ini, saya tidak akan mempertimbangkan cara untuk memperbaiki masalah ini, tetapi saya akan memberi tahu satu hal – dalam kasus saya, tidak mungkin untuk memperbaiki pembuatan kueri dalam aplikasi.
Sebagai gantinya, saya ingin menganalisis alasan mengapa SQL Server membangun rencana kueri begitu lama. Dalam DBMS apa pun, termasuk SQL Sever, masalah pengoptimalan utama adalah metode menggabungkan tabel satu sama lain. Selain metode join, urutan tabel join juga sangat penting.
Mari kita bicara tentang urutan tabel bergabung secara rinci. Sangat penting untuk memahami bahwa kemungkinan jumlah tabel bergabung tumbuh secara eksponensial, tidak linier. Contoh Fox, hanya ada 2 metode yang mungkin untuk menggabungkan 2 tabel, dan jumlahnya bisa mencapai 12 metode untuk 3 tabel. Urutan gabungan yang berbeda dapat memiliki biaya kueri yang berbeda, dan pengoptimal SQL Server harus memilih metode yang paling optimal. Tetapi ketika jumlah tabel tinggi, itu menjadi tugas yang intensif sumber daya. Jika SQL Server mulai memeriksa semua varian yang mungkin, kueri tersebut mungkin tidak akan pernah dieksekusi. Itu sebabnya, SQL Server tidak pernah melakukannya dan selalu mencari rencana yang cukup bagus, bukan rencana terbaik. SQL Server selalu mencoba mencapai kompromi antara waktu eksekusi dan kualitas paket.
Berikut adalah contoh pertumbuhan eksponensial dari metode join. SQL Server dapat memilih berbagai metode bergabung (pohon kiri-dalam, kanan-dalam, lebat). Secara visual, tampilannya sebagai berikut:
Tabel di bawah ini menunjukkan kemungkinan metode join ketika jumlah tabel bertambah:
Anda bisa mendapatkan nilai ini sendiri:
Untuk kedalaman kiri: 5! =5 x 4 x 3 x 2 x 1 =120
Untuk pohon lebat: (2n–2)!/(n–1)!
Kesimpulan :Berikan perhatian khusus pada jumlah GABUNG dan jangan menghalangi pengoptimal. Jika Anda gagal mendapatkan hasil yang diinginkan dalam kueri yang berisi banyak GABUNG, bagi menjadi beberapa kueri kecil dan Anda akan terkejut dengan hasilnya.
P.S. Tentu saja, kita harus memahami bahwa selain menentukan urutan gabungan tabel, pengoptimal kueri juga harus memilih jenis gabungan, metode akses data (Scan, Seek) dll.
Produk yang berguna:
SQL Complete – tulis, percantik, perbaiki kode Anda dengan mudah, dan tingkatkan produktivitas Anda.