Taramaya neden olan kalıcı hesaplanmış sütun


9

Düzenli bir sütunu kalıcı bir hesaplanmış sütuna dönüştürmek, bu sorgunun dizin aramaları yapamamasına neden oluyor. Neden?

2016 SP1 CU1 dahil olmak üzere çeşitli SQL Server sürümlerinde test edilmiştir.

Repros

Sorun ile table1, col7.

Tablolar ve sorgu, orijinallerin kısmi (ve basitleştirilmiş) bir versiyonudur. Sorgunun farklı şekilde yeniden yazılabileceğini ve bir nedenden dolayı problemden kaçınabileceğinin farkındayım, ancak koda dokunmaktan kaçınmalıyız ve neden table1aranamayacağı sorusu hala duruyor.

Paul White'ın gösterdiği gibi (teşekkürler!), Zorlama durumunda arama yapılabilir, bu yüzden soru: Arama neden optimize edici tarafından seçilmiyor ve aramayı değiştirmeden yapmak için olması gerektiği gibi farklı bir şey yapıp yapamayacağımız. şifresi?

Sorunlu kısmı açıklığa kavuşturmak için, kötü yürütme planındaki ilgili tarama:

plan

Yanıtlar:


12

Arama neden optimize edici tarafından seçilmiyor?


TL: DR Genişletilmiş hesaplanmış sütun tanımı, optimize edicinin başlangıçta eklemleri yeniden sıralama yeteneğini etkiler. Farklı bir başlangıç ​​noktasıyla, maliyete dayalı optimizasyon optimizerde farklı bir yol izler ve farklı bir nihai plan seçeneğiyle sonuçlanır.


ayrıntılar

En basit sorgular dışındaki herkes için, optimizer olası planların tüm alanı gibi bir şey keşfetmeye çalışmaz. Bunun yerine, makul görünümlü bir başlangıç ​​noktası seçer , ardından makul bir plan bulana kadar bir veya daha fazla arama aşamasında mantıksal ve fiziksel varyasyonları keşfetmek için bütçelenmiş bir çaba harcar.

İki durum için farklı planlar (farklı nihai maliyet tahminleri ile) elde etmenizin ana nedeni, farklı başlangıç ​​noktalarının olmasıdır. Farklı bir yerden başlayarak, optimizasyon farklı bir yerde sona erer (sınırlı sayıda keşif ve uygulama yinelemesinden sonra). Umarım bu oldukça sezgiseldir.

Başlangıç noktası I bahsedilen biraz terimi metinsel temsil dayanmaktadır, ancak değişiklikler bu normalleşmeyi bağlanma ayrıştırma boyunca geçerken iç ağaç gösterimi yapılan ve sorgu derleme basitleştirilmesi aşamaları vardır.

Daha da önemlisi, kesin başlangıç ​​noktası büyük ölçüde optimize edici tarafından seçilen ilk birleşme sırasına bağlıdır . Bu seçim, istatistikler yüklenmeden önce ve herhangi bir kardinalite tahmini türetilmeden önce yapılır. Bununla birlikte, her bir tablodaki toplam kardinalite (satır sayısı), sistem meta verilerinden elde edilmiş olarak bilinir.

İlk birleşme sırası bu nedenle buluşsal yöntemlere dayanır . Örneğin, iyileştirici ağacı daha küçük tabloların daha büyük olanlardan önce birleştirileceği ve iç birleşimler dış birleşimlerin (ve çapraz birleşimlerin) önüne geleceği şekilde yeniden yazmaya çalışır.

Hesaplanan sütunun varlığı, özellikle işlemcinin dış birleşimleri sorgu ağacında aşağı itme yeteneğiyle bu işleme müdahale eder. Bunun nedeni, hesaplanan sütunun birleştirme yeniden sıralaması gerçekleşmeden önce temel ifadesine genişletilmesidir ve bir birleştirmeyi karmaşık bir ifadenin ötesine taşımak, basit bir sütun başvurusunu geçmekten çok daha zordur.

İlgili ağaçlar oldukça büyüktür, ancak açıklamak için hesaplanmamış sütun ilk sorgu ağacı şununla başlar: (üstteki iki dış birleşime dikkat edin)

LogOp_Select
    LogOp_Apply (x_jtLeftOuter) 
        LogOp_LeftOuterJoin
            LogOp_NAryJoin
                LogOp_LeftAntiSemiJoin
                    LogOp_NAryJoin
                        LogOp_GBL'yi al: dbo.table1 (TBL: a4 takma adı)
                        LogOp_Select
                            LogOp_Get TBL: dbo.table6 (TBL: a3 takma adı)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL: [a3] .col18
                                ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                        LogOp_Select
                            LogOp_GT TBL'si alın: dbo.table1 (TBL: a1 takma adı)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL: [a1] .col2
                                ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                        LogOp_Select
                            LogOp_GT TBL'si alın: dbo.table5 (TBL: a2 takma adı)
                            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_GT TBL'si alın: dbo.table7 (TBL: a7 takma adı)
                        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 (TBL diğer adı: cdc)
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [cdc] .col6
                        ScaOp_Const TI (smallint, ML = 2) XVAR (smallint, Sahip değil, Değer = 4)
                LogOp_GT TBL'si alın: dbo.table5 (TBL: a5 takma adı) 
                LogOp_Get TBL: table2 (diğer ad 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 (TBL takma adı: ahcr)
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier QCOL: [ahcr] .col9
                ScaOp_Identifier QCOL: [cdt] .col1

Hesaplanan sütun sorgusunun aynı parçası : (dış birleşimin çok daha aşağı olduğunu, genişletilmiş hesaplanmış sütun tanımını ve (iç) birleşim sıralamasındaki diğer bazı küçük farkları not edin)

LogOp_Select
    LogOp_Apply (x_jtLeftOuter)
        LogOp_NAryJoin
            LogOp_LeftAntiSemiJoin
                LogOp_NAryJoin
                    LogOp_GT TBL'si alın: dbo.table1 (TBL: a4 takma adı)
                    LogOp_Select
                        LogOp_Get TBL: dbo.table6 (TBL: a3 takma adı)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [a3] .col18
                            ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                    LogOp_Select
                        LogOp_GT TBL'si alın: dbo.table1 (TBL: a1 takma adı
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [a1] .col2
                            ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                    LogOp_Select
                        LogOp_GT TBL'si alın: dbo.table5 (TBL: a2 takma adı)
                        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_GT TBL'si alın: dbo.table7 (TBL: a7 takma adı) 
                    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 (TBL diğer adı: cdc) 
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL: [cdc] .col6
                                ScaOp_Const TI (smallint, ML = 2) XVAR (smallint, Sahip değil, Değer = 4)
                        LogOp_Get TBL: table2 (diğer ad TBL: cdt) 
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [cdc] .col1
                            ScaOp_Identifier QCOL: [cdt] .col1
                    LogOp_Get TBL: table3 (TBL takma adı: ahcr) 
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [ahcr] .col9
                        ScaOp_Identifier QCOL: [cdt] .col1
                AncOp_PrjList 
                    AncOp_PrjEl QCOL: [cdc] .col7
                        ScaOp_Convert karakter harmanı 53256, Null, Trim, ML = 6
                            ScaOp_IIF varchar harman 53256, Null, Var, Trim, ML = 6
                                ScaOp_Comp x_cmpEq
                                    ScaOp_Intrinsic isnumeric
                                        ScaOp_Intrinsic hakkı
                                            ScaOp_Identifier QCOL: [cdc] .col4
                                            ScaOp_Const TI (int, ML = 4) XVAR (int, Sahip Olulmamış, Değer = 4)
                                    ScaOp_Const TI (int, ML = 4) XVAR (int, Sahip Olulmamış, Değer = 0)
                                ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 1) XVAR (varchar, Sahip olunan, Değer = Len, Veri = (0,))
                                ScaOp_Intrinsic alt dize
                                    ScaOp_Const TI (int, ML = 4) XVAR (int, Sahip Olulmamış, Değer = 6)
                                    ScaOp_Const TI (int, ML = 4) XVAR (int, Sahip Olulmamış, Değer = 1)
                                    ScaOp_Identifier QCOL: [cdc] .col4
            LogOp_GT TBL'si alın: dbo.table5 (TBL: a5 takma adı)
            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

İstatistikler yüklenir ve ilk birleştirme sırası ayarlandıktan hemen sonra ağaç üzerinde bir ilk kardinalite tahmini gerçekleştirilir. Birleşimlerin farklı sıralarda olması da bu tahminleri etkiler ve bu nedenle daha sonraki maliyete dayalı optimizasyon sırasında devirme etkisi vardır.

Son olarak, bu bölüm için, ağacın ortasına takılmış bir dış birleştirme olması, maliyete dayalı optimizasyon sırasında bazı yeniden birleştirme kurallarının eşleşmesini önleyebilir.


Bir plan kılavuzu (veya aynı şekilde bir USE PLANipucu - sorgunuz için örnek ) kullanmak , arama stratejisini , sağlanan şablonun genel şekli ve özellikleri tarafından yönlendirilen daha hedefe yönelik bir yaklaşımla değiştirir . İyileştirici açıklıyor olabilir aynı bulmak table1bir plan kılavuzu veya ipucu kullanıldığında, hem bilgisayarlı ve olmayan hesaplanan sütun şemaları karşı planı ararlar.

Arayışı gerçekleştirmek için farklı bir şey yapıp yapamayacağımız

Bu, yalnızca optimize edici kendi başına kabul edilebilir performans özelliklerine sahip bir plan bulamazsa endişelenmeniz gereken bir şeydir.

Tüm normal ayar araçları potansiyel olarak uygulanabilir. Örneğin, sorguyu daha basit parçalara ayırabilir, kullanılabilir endekslemeyi gözden geçirebilir ve geliştirebilir, yeni istatistikleri güncelleyebilir veya oluşturabilirsiniz ... vb.

Bütün bunlar kardinalite tahminlerini, optimize ediciden alınan kod yolunu ve maliyete dayalı kararları süptil yollarla etkileyebilir.

Sonuçta ipuçlarını (veya bir plan kılavuzunu) kullanmaya başvurabilirsiniz, ancak bu genellikle ideal çözüm değildir.


Yorumlardan ek sorular

Sorgu vb basitleştirmek için en iyi olduğunu kabul ediyorum, ama optimizer optimizasyon ile devam ve aynı sonuca ulaşmak için bir yol (izleme bayrağı) var mı?

Hayır, kapsamlı bir arama yapmak için hiçbir izleme bayrağı yoktur ve bir tane istemezsiniz. Olası arama alanı çok geniştir ve evrenin yaşını aşan derleme süreleri iyi karşılanmaz. Ayrıca, optimizer olası her mantıksal dönüşümü bilmiyor (kimse bilmiyor).

Ayrıca, sütun devam ettiği için neden karmaşık genişleme gerekiyor? Optimize edici neden onu genişletmekten kaçınmıyor, normal bir sütun gibi davranıyor ve aynı başlangıç ​​noktasına ulaşamıyor?

Ek optimizasyon fırsatlarını etkinleştirmek için hesaplanan sütunlar genişletilir (görünümler gibi). Genişletme işlemin ilerleyen bölümlerinde örneğin kalıcı bir sütun veya dizine geri eşleştirilebilir, ancak bu ilk birleştirme sırası sabitlendikten sonra gerçekleşir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.