Bunu biliyorum COALESCE
Birkaç sütun üzerinde ve bunlara katılmanın iyi bir uygulama olmadığını .
Şema 3NF + (anahtarlar ve kısıtlamalar ile) olduğunda ve sorgu ilişkisel ve öncelikle SPJG (seçim-projeksiyon-birleştirme-gruba göre) olduğunda iyi kardinalite ve dağıtım tahminleri üretmek yeterince zordur. CE modeli bu ilkeler üzerine inşa edilmiştir. Bir sorguda ne kadar olağandışı veya ilişkisel olmayan özellikler varsa, kardinalite ve seçicilik çerçevesinin ele alabileceği sınırlara yaklaşır. Çok ileri git ve CE pes edecek ve tahmin edecek .
MCVE örneğinin çoğu, basit iç equijoin (veya semijoin) yerine ağırlıklı olarak dış equijoins (iç birleşim artı anti-semijoin olarak modellenmiş) olsa da basit SPJ'dir (G yok). Tüm ilişkilerin anahtarları vardır, ancak yabancı anahtarlar veya başka kısıtlamalar yoktur. Birleşimler dışında hepsi bire çok, bu iyi.
İstisnadır birçok çoğa dış arasına katılmak X_DETAIL_1
ve X_DETAIL_LINK
. MCVE'deki bu birleştirmenin tek işlevi, satırları potansiyel olarak çoğaltmaktır X_DETAIL_1
. Bu alışılmadık bir şey bir şey.
Basit eşitlik tahminleri (seçimler) ve skaler operatörler de daha iyidir. Örneğin, karşılaştır-eşit öznitelik / sabit özniteliği modelde normalde iyi çalışır. Bu tür tahminlerin uygulanmasını yansıtacak şekilde histogramları ve frekans istatistiklerini değiştirmek nispeten "kolaydır".
COALESCE
üzerine kurulu CASE
, sırayla olarak dahili olarak uygulandığı IIF
(ve bu çok önce doğruydu IIF
Transact-SQL dilinde çıktı). CE modelleri IIF
bir şekildeUNION
, her biri giriş ilişkisi üzerine bir seçim üzerine bir projeden oluşan, birbirini dışlayan iki çocuklu . Listelenen bileşenlerin her birinin model desteği vardır, bu nedenle bunları birleştirmek nispeten kolaydır. Yine de, ne kadar çok katman soyutlaması olursa, sonuç o kadar az doğrudur - daha büyük uygulama planlarının daha az kararlı ve güvenilir olma eğiliminde olmasının bir nedeni.
ISNULL
Öte yandan, bir içsel motora. Daha temel bileşenler kullanılarak oluşturulmaz. ISNULL
Örneğin, bir histograma efekt uygulamak, NULL
değerler için adımın değiştirilmesi (ve gerektiğinde sıkıştırma) kadar basittir . Skaler operatörler gittikçe nispeten opaktır ve mümkün olan en iyi şekilde önlenir. Bununla birlikte, genellikle daha iyi optimizasyon dostu (daha az optimize edici dostu olmayan)CASE
temelli bir alternatiften .
CE (70 ve 120+), SQL Server standartlarına göre bile çok karmaşıktır. Her operatöre basit bir mantık (gizli formülle) uygulamak söz konusu değildir. CE anahtarları ve fonksiyonel bağımlılıkları bilir; frekansları, çok değişkenli istatistikleri ve histogramları kullanarak nasıl tahmin edileceğini bilir; ve tonlarca özel durum, iyileştirme, kontrol ve bakiye ve destekleyici yapılar var. Sık sık, örneğin, çoklu yollarla (frekans, histogram) birleşmeleri tahmin eder ve ikisi arasındaki farklara dayanarak bir sonuç veya düzenlemeye karar verir.
Ele alınması gereken son bir temel şey: Başlangıç kardinalite tahmini, sorgu ağacındaki her işlem için aşağıdan yukarıya doğru çalışır. Önce yaprak operatörleri için seçicilik ve kardinalite elde edilir (temel ilişkiler). Ana operatörler için değiştirilmiş histogramlar ve yoğunluk / frekans bilgileri türetilir. Ağaca ne kadar yaklaşırsak, hataların birikme eğilimi olduğu için tahminlerin kalitesi o kadar düşük olur.
Bu tek kapsamlı kapsamlı tahmin, bir başlangıç noktası sağlar ve nihai bir yürütme planına herhangi bir dikkat verilmeden önce gerçekleşir (önemsiz plan derleme aşamasından bile önce bir şekilde gerçekleşir). Bu noktada sorgu ağacı, sorgunun yazılı biçimini oldukça yakından yansıtma eğilimindedir (alt sorgular kaldırılmış ve basitleştirmeler uygulanmış olsa da).
İlk tahminden hemen sonra, SQL Server sezgisel birleştirme yeniden sıralaması gerçekleştirir; Ayrıca iç birleşimleri dış birleşmelerden ve çapraz ürünlerden önce konumlandırmaya çalışır. Yetenekleri kapsamlı değildir; çabaları kapsamlı değildir; ve fiziksel maliyetleri dikkate almaz (henüz mevcut olmadıkları için - sadece istatistiksel bilgiler ve meta veri bilgileri mevcuttur). Sezgisel yeniden sıralama, basit iç equijoin ağaçlarında en başarılı olanıdır. Maliyete dayalı optimizasyon için "daha iyi" bir başlangıç noktası sağlamak için vardır.
Bu birleşme kardinalite tahmini neden bu kadar büyük?
MCVE bir "olağandışı" çoğunlukla yedekli sahiptir birçok çoğa katılmak ve bir eş ile katılmak COALESCE
yüklemi. Operatör ağacı da bir iç birleşim sahip son sezgisel yeniden sıralama daha tercih edilen bir konuma ağacı hareket edemedi birleştiği. Tüm skaler ve projeksiyonları bir kenara bırakarak, birleştirme ağacı:
LogOp_Join [ Card=4.52803e+009 ]
LogOp_LeftOuterJoin [ Card=481577 ]
LogOp_LeftOuterJoin [ Card=481577 ]
LogOp_LeftOuterJoin [ Card=481577 ]
LogOp_LeftOuterJoin [ Card=481577 ]
LogOp_Get TBL: X_DRIVING_TABLE(alias TBL: dt) [ Card=481577 ]
LogOp_Get TBL: X_DETAIL_1(alias TBL: d1) [ Card=70 ]
LogOp_Get TBL: X_DETAIL_LINK(alias TBL: lnk) [ Card=47 ]
LogOp_Get TBL: X_DETAIL_2(alias TBL: d2) X_DETAIL_2 [ Card=119 ]
LogOp_Get TBL: X_DETAIL_3(alias TBL: d3) X_DETAIL_3 [ Card=281 ]
LogOp_Get TBL: X_LAST_TABLE(alias TBL: lst) X_LAST_TABLE [ Card=94025 ]
Hatalı nihai tahminin zaten mevcut olduğuna dikkat edin. Card=4.52803e+009
Dahili olarak çift hassasiyetli kayan nokta değeri 4.5280277425e + 9 (ondalık olarak 4528027742.5) olarak yazdırılır ve dahili olarak saklanır.
Özgün sorgudaki türetilmiş tablo kaldırıldı ve projeksiyonlar normalleştirildi. İlk kardinalite ve seçicilik tahmininin yapıldığı ağacın SQL gösterimi:
SELECT
PRIMARY_ID = COALESCE(d1.JOIN_ID, d2.JOIN_ID, d3.JOIN_ID)
FROM X_DRIVING_TABLE dt
LEFT OUTER JOIN X_DETAIL_1 d1
ON dt.ID = d1.ID
LEFT OUTER JOIN X_DETAIL_LINK lnk
ON d1.LINK_ID = lnk.LINK_ID
LEFT OUTER JOIN X_DETAIL_2 d2
ON dt.ID = d2.ID
LEFT OUTER JOIN X_DETAIL_3 d3
ON dt.ID = d3.ID
INNER JOIN X_LAST_TABLE lst
ON lst.JOIN_ID = COALESCE(d1.JOIN_ID, d2.JOIN_ID, d3.JOIN_ID)
(Bir taraf olarak, tekrarlananlar COALESCE
da nihai planda bulunur - bir kez nihai Hesaplama Skalerinde ve bir kez iç birleşimin iç tarafında).
Son birleştirmeye dikkat edin. Bu iç birleşim (tanım gereği) uygulanan X_LAST_TABLE
bir seçim (birleşim yüklemesi) ile kartezyen ürünü ve önceki birleşim çıktısıdır lst.JOIN_ID = COALESCE(d1.JOIN_ID, d2.JOIN_ID, d3.JOIN_ID)
. Kartezyen ürünün asıl özelliği sadece 481577 * 94025 = 45280277425'dir.
Bunun için yüklemin seçiciliğini belirlemeli ve uygulamalıyız. Opak genişletilmiş kombinasyonu COALESCE
(açısından ağaç UNION
ve IIF
önemli bilgileri üzerindeki etki ile birlikte, unutmayın), elde edilen histogramlar ve daha önce "sıradışı" çok artıklı çok-sayıda dış sıklıkları birleştirilmiş araçları birleştirme CE yapamaz normal yollardan herhangi birinde kabul edilebilir bir tahmin elde edebilir.
Sonuç olarak, Tahmin Mantığına girer. Tahmin mantığı orta derecede karmaşıktır, "eğitimli" tahmin katmanları ve "çok eğitilmemiş" tahmin algoritmaları denenmiştir. Bir tahmin için daha iyi bir temel bulunamazsa, model bir eşitlik karşılaştırması için son çare tahminini kullanır: sqllang!x_Selectivity_Equal
= sabit 0.1 seçicilik (% 10 tahmin):
-- the moment of doom
movsd xmm0,mmword ptr [sqllang!x_Selectivity_Equal
Sonuç, kartezyen üründe 0.1 seçiciliktir: 481577 * 94025 * 0.1 = 4528027742.5 (~ 4.52803e + 009), daha önce belirtildiği gibi.
Yeniden yazımlar
Sorunlu birleşme yorumlandığında , daha iyi bir tahmin üretilir çünkü sabit son seçicilik "son çare tahmini" önlenir (temel bilgiler 1-M birleşimleri tarafından saklanır). Tahminin kalitesi hala düşük güvenlidir, çünkü bir COALESCE
birleştirme yüklemesi CE dostu değildir. Gözden geçirilmiş tahmin, en azından insanlar için daha makul görünüyor .
Sorgu X_DETAIL_LINK
son yerleştirilecek dış birleşim ile yazıldığında , sezgisel yeniden sıralama, son iç birleşim ile değiştirebilir X_LAST_TABLE
. İç birleşimi problemin hemen dış birleşimine yerleştirmek, erken yeniden sıralamanın sınırlı yeteneklerini nihai tahminin iyileştirilmesi için fırsat verir, çünkü çoğunlukla gereksiz "olağandışı" çoktan çoğa dış birleşmenin etkileri zor seçicilik tahmininden sonra gelir için COALESCE
. Yine, tahminler sabit tahminlerden biraz daha iyidir ve muhtemelen bir mahkemede belirlenen çapraz incelemeye dayanmaz.
İç ve dış birleşimlerin bir karışımını yeniden düzenlemek zor ve zaman alıcıdır (aşama 2 tam optimizasyon bile sadece sınırlı bir teorik hareket alt kümesini dener).
ISNULL
Max Vernon'un cevabında önerilen yuvalanmış , kefaletle sabit tahminden kaçınmayı başarır, ancak nihai tahmin, olası bir sıfır satırdır (ahlaksızlık için bir satıra yükseltilmiş). Bu, hesaplamanın sahip olduğu tüm istatistiksel temeller için 1 satırlık sabit bir tahmin olabilir.
0 ve 481577 satır arasında birleştirme kardinalite tahmini beklenir.
Fiziksel olarak farklı, ancak mantıksal ve anlamsal olarak özdeş alt ağaçlarda farklı zamanlarda (maliyete dayalı optimizasyon sırasında) kardinalite tahmininin gerçekleşebileceğini kabul etse bile bu makul bir beklentidir - son plan bir çeşit dikişli en iyisidir. en iyi (not grubu başına). Plan çapında tutarlılık garantisinin olmaması, bireysel bir katılımın saygınlığı savurabileceği anlamına gelmez, anladım.
Öte yandan, eğer son çare tahmininde kalırsak , umut zaten kaybolur, öyleyse neden rahatsız. Bildiğimiz tüm püf noktaları denedik ve vazgeçtik. Başka bir şey yoksa, vahşi son tahmin, bu sorgunun derlenmesi ve optimizasyonu sırasında her şeyin CE içinde iyi gitmediğine dair büyük bir uyarı işaretidir.
MCVE'yi denediğimde, 120+ CE ISNULL
, orijinal sorgu için sıfır (= 1) satır nihai tahmini (iç içe geçmiş gibi) üretti .
Gerçek çözüm, muhtemelen COALESCE
veya olmadan basit ek birleşimlere ISNULL
ve ideal olarak yabancı anahtarlar ve sorgu derlemesi için faydalı diğer kısıtlamalara izin veren bir tasarım değişikliğini içerir .
bigint
Bunun yerine herhangi bir şekilde geçiş yapabiliyorsanızdecimal(18, 0)
avantaj elde edersiniz: 1) her değer için 9 yerine 8 bayt kullanın ve 2) sonuçları olabilecek paket veri türü yerine baytla karşılaştırılabilir bir veri türü kullanın değerleri karşılaştırırken CPU zamanı için.