Mengapa pencarian tidak dipilih oleh pengoptimal
TL: DR Definisi kolom terhitung yang diperluas mengganggu kemampuan pengoptimal untuk menyusun ulang bergabung pada awalnya. Dengan titik awal yang berbeda, pengoptimalan berbasis biaya mengambil jalur yang berbeda melalui pengoptimal, dan berakhir dengan pilihan rencana akhir yang berbeda.
Detail
Untuk semua pertanyaan kecuali yang paling sederhana, pengoptimal tidak berupaya mengeksplorasi apa pun seperti seluruh ruang rencana yang mungkin. Alih-alih, ia memilih titik awal yang tampak masuk akal , lalu menghabiskan sejumlah upaya yang dianggarkan untuk mengeksplorasi variasi logis dan fisik, dalam satu atau beberapa fase pencarian, hingga menemukan rencana yang masuk akal.
Alasan utama Anda mendapatkan paket berbeda (dengan taksiran biaya akhir berbeda) untuk kedua kasus tersebut adalah karena ada titik awal yang berbeda . Mulai dari tempat yang berbeda, pengoptimalan berakhir di tempat yang berbeda (setelah jumlah iterasi eksplorasi dan implementasi yang terbatas). Saya harap ini cukup intuitif.
Titik awal yang saya sebutkan, agak didasarkan pada representasi tekstual dari kueri, tetapi perubahan dilakukan pada representasi internal tree ketika melewati tahapan parsing, binding, normalisasi, dan penyederhanaan kompilasi query.
Yang penting, titik awal yang tepat sangat bergantung pada urutan bergabung awal yang dipilih oleh pengoptimal. Pilihan ini dibuat sebelum statistik dimuat, dan sebelum estimasi kardinalitas apa pun telah diturunkan. Kardinalitas total (jumlah baris) di setiap tabel diketahui, telah diperoleh dari metadata sistem.
Pemesanan bergabung awal karena itu didasarkan pada heuristik . Misalnya, pengoptimal mencoba untuk menulis ulang pohon sedemikian rupa sehingga tabel yang lebih kecil bergabung sebelum yang lebih besar, dan sambungan dalam datang sebelum sambungan luar (dan gabungan silang).
Kehadiran kolom yang dihitung mengganggu proses ini, paling khusus dengan kemampuan pengoptimal untuk mendorong luar bergabung ke bawah pohon kueri. Ini karena kolom yang dikomputasi diperluas ke ekspresi yang mendasarinya sebelum penggabungan ulang terjadi, dan memindahkan gabungan melewati ekspresi kompleks jauh lebih sulit daripada memindahkannya melewati referensi kolom sederhana.
Pohon-pohon yang terlibat cukup besar, tetapi untuk menggambarkan, pohon permintaan awal kolom non-dihitung dimulai dengan: (perhatikan dua gabungan luar di atas)
LogOp_Select
LogOp_Apply (x_jtLeftOuter)
LogOp_LeftOuterJoin
LogOp_NAryJoin
LogOp_LeftAntiSemiJoin
LogOp_NAryJoin
LogOp_Get TBL: dbo.table1 (alias TBL: a4)
LogOp_Select
LogOp_Get TBL: dbo.table6 (alias TBL: a3)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a3] .col18
ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
LogOp_Select
LogOp_Get TBL: dbo.table1 (alias TBL: a1)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a1] .col2
ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
LogOp_Select
LogOp_Get TBL: dbo.table5 (alias TBL: a2)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a2] .col2
ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a4] .col2
ScaOp_Identifier QCOL: [a3] .col19
LogOp_Select
LogOp_Get TBL: dbo.table7 (alias TBL: a7)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a7] .col22
ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a4] .col2
ScaOp_Identifier QCOL: [a7] .col23
LogOp_Select
LogOp_Get TBL: table1 (alias TBL: cdc)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [cdc] .col6
ScaOp_Const TI (smallint, ML = 2) XVAR (smallint, Tidak Dimiliki, Nilai = 4)
LogOp_Get TBL: dbo.table5 (alias TBL: a5)
LogOp_Get TBL: table2 (alias TBL: cdt)
ScaOp_Logical x_lopAnd
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a5] .col2
ScaOp_Identifier QCOL: [cdc] .col2
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a4] .col2
ScaOp_Identifier QCOL: [cdc] .col2
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [cdt] .col1
ScaOp_Identifier QCOL: [cdc] .col1
LogOp_Get TBL: table3 (alias TBL: ahcr)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [ahcr] .col9
ScaOp_Identifier QCOL: [cdt] .col1
Fragmen yang sama dari kueri kolom yang dihitung adalah: (perhatikan join luar jauh lebih rendah ke bawah, definisi kolom terkomputasi yang diperluas, dan beberapa perbedaan halus lainnya dalam pemesanan join (dalam))
LogOp_Select
LogOp_Apply (x_jtLeftOuter)
LogOp_NAryJoin
LogOp_LeftAntiSemiJoin
LogOp_NAryJoin
LogOp_Get TBL: dbo.table1 (alias TBL: a4)
LogOp_Select
LogOp_Get TBL: dbo.table6 (alias TBL: a3)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a3] .col18
ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
LogOp_Select
LogOp_Get TBL: dbo.table1 (alias TBL: a1
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a1] .col2
ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
LogOp_Select
LogOp_Get TBL: dbo.table5 (alias TBL: a2)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a2] .col2
ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a4] .col2
ScaOp_Identifier QCOL: [a3] .col19
LogOp_Select
LogOp_Get TBL: dbo.table7 (alias TBL: a7)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a7] .col22
ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a4] .col2
ScaOp_Identifier QCOL: [a7] .col23
LogOp_Project
LogOp_LeftOuterJoin
LogOp_Join
LogOp_Select
LogOp_Get TBL: table1 (alias TBL: cdc)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [cdc] .col6
ScaOp_Const TI (smallint, ML = 2) XVAR (smallint, Tidak Dimiliki, Nilai = 4)
LogOp_Get TBL: table2 (alias TBL: cdt)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [cdc] .col1
ScaOp_Identifier QCOL: [cdt] .col1
LogOp_Get TBL: table3 (alias TBL: ahcr)
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [ahcr] .col9
ScaOp_Identifier QCOL: [cdt] .col1
AncOp_PrjList
AncOp_PrjEl QCOL: [cdc] .col7
ScaOp_Convert char collate 53256, Null, Trim, ML = 6
ScaOp_IIF varchar menyusun 53256, Null, Var, Trim, ML = 6
ScaOp_Comp x_cmpEq
ScaOp_Intrinsic isnumeric
ScaOp_Intrinsik benar
ScaOp_Identifier QCOL: [cdc] .col4
ScaOp_Const TI (int, ML = 4) XVAR (int, Tidak Dimiliki, Nilai = 4)
ScaOp_Const TI (int, ML = 4) XVAR (int, Tidak Dimiliki, Nilai = 0)
ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 1) XVAR (varchar, Dimiliki, Nilai = Len, Data = (0,))
Substring ScaOp_Intrinsik
ScaOp_Const TI (int, ML = 4) XVAR (int, Tidak Dimiliki, Nilai = 6)
ScaOp_Const TI (int, ML = 4) XVAR (int, Tidak Dimiliki, Nilai = 1)
ScaOp_Identifier QCOL: [cdc] .col4
LogOp_Get TBL: dbo.table5 (alias TBL: a5)
ScaOp_Logical x_lopAnd
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a5] .col2
ScaOp_Identifier QCOL: [cdc] .col2
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [a4] .col2
ScaOp_Identifier QCOL: [cdc] .col2
Statistik dimuat dan estimasi kardinalitas awal dilakukan di pohon tepat setelah pesanan bergabung awal ditetapkan. Bergabung dalam pesanan berbeda juga memengaruhi taksiran ini, dan juga memiliki efek ketukan selama pengoptimalan berbasis biaya nanti.
Akhirnya untuk bagian ini, memiliki sambungan luar yang macet di tengah-tengah pohon dapat mencegah beberapa aturan bergabung lagi yang cocok selama optimasi berbasis biaya.
Dengan menggunakan panduan rencana (atau, USE PLAN
petunjuk yang setara - contoh untuk permintaan Anda ) mengubah strategi pencarian menjadi pendekatan yang lebih berorientasi pada tujuan, dipandu oleh bentuk umum dan fitur-fitur templat yang disediakan. Ini menjelaskan mengapa pengoptimal dapat menemukan table1
rencana pencarian yang sama terhadap skema kolom yang dihitung dan yang tidak dikomputasi, ketika panduan atau petunjuk rencana digunakan.
Apakah kita dapat melakukan sesuatu yang berbeda untuk mewujudkannya
Ini adalah sesuatu yang Anda hanya perlu khawatir jika pengoptimal tidak menemukan rencana dengan karakteristik kinerja yang dapat diterima sendiri.
Semua alat penyetelan normal berpotensi berlaku. Anda dapat, misalnya, memecah kueri menjadi bagian-bagian yang lebih sederhana, meninjau dan meningkatkan pengindeksan yang tersedia, memperbarui atau membuat statistik baru ... dan seterusnya.
Semua hal ini dapat memengaruhi perkiraan kardinalitas, jalur kode yang diambil melalui pengoptimal, dan memengaruhi keputusan berbasis biaya dengan cara yang halus.
Anda mungkin akhirnya menggunakan petunjuk (atau panduan rencana), tetapi itu biasanya bukan solusi yang ideal.
Pertanyaan tambahan dari komentar
Saya setuju bahwa yang terbaik adalah menyederhanakan kueri dll, tetapi apakah ada cara (jejak bendera) untuk membuat pengoptimal melanjutkan dengan optimasi dan mencapai hasil yang sama?
Tidak, tidak ada tanda jejak untuk melakukan pencarian lengkap, dan Anda tidak menginginkannya. Ruang pencarian yang mungkin luas, dan waktu kompilasi yang melampaui usia alam semesta tidak akan diterima dengan baik. Juga, pengoptimal tidak tahu setiap kemungkinan transformasi logis (tidak ada yang tahu).
Juga, mengapa ekspansi kompleks diperlukan, karena kolom itu tetap ada? Mengapa pengoptimal tidak dapat menghindari mengembangkannya, memperlakukannya seperti kolom biasa, dan mencapai titik awal yang sama?
Kolom yang dikomputasi diperluas (seperti halnya tampilan) untuk mengaktifkan peluang optimisasi tambahan. Ekspansi dapat dicocokkan kembali ke misalnya kolom atau indeks yang bertahan kemudian dalam proses, tetapi ini terjadi setelah pesanan gabungan awal diperbaiki.