Yanlış bir şey mi yapıyoruz yoksa SQL Server hatası mı?
Her zamanki destek kanalınız üzerinden bildirmeniz gereken yanlış sonuçlardan oluşan bir hatadır. Bir destek sözleşmeniz yoksa, Microsoft davranışı bir hata olarak onaylarsa, ödemeli olayların normalde geri ödendiğini bilmek yardımcı olabilir .
Böcek üç bileşen gerektirir:
- Dış referansı olan iç içe döngüler (bir başvuru)
- Dış referansı arayan bir iç taraf tembel indeks makarası
- Bir iç taraf birleştirme işleci
Örneğin, sorudaki sorgu aşağıdakine benzer bir plan oluşturur:
Bu öğelerden birini kaldırmanın birçok yolu vardır, böylece hata artık çoğaltılmaz.
Örneğin, bir kişi optimize edicinin Lazy Index Spool kullanmamayı seçtiği anlamına gelen endeksler veya istatistikler yaratabilir. Veya bir birleştirme veya birleştirme birliğini zorlamak için Birleştirme'yi kullanmak yerine ipuçlarını kullanabilir. Biri aynı semantiği ifade etmek için sorguyu da yeniden yazabilir, ancak gerekli elemanlardan bir veya daha fazlasının eksik olduğu farklı bir plan şeklinde sonuçlanır.
Daha fazla detay
Bir Lazy Index Spool, dış referans (ilişkili parametre) değerleri ile indekslenmiş bir çalışma tablosunda, iç taraf sonuç satırlarını tembel bir şekilde önler. Eğer bir Lazy Index Spool'dan daha önce gördüğü bir dış referans istenirse, önbelleğe alınmış sonuç satırını çalışma masasından alır ("geri sar"). Makaraya daha önce görmediği bir dış referans değeri istenirse, alt ağacını geçerli dış referans değeriyle çalıştırır ve sonucu önbelleğe alır (bir "yeniden bağlama"). Lazy Index Spool'daki arama yordamı, çalışma tablosu için anahtar (lar) ı gösterir.
Sorun, bu özel plan şeklinde, makara yeni bir dış referansın daha önce gördüğü ile aynı olup olmadığını görmek için kontrol ettiğinde ortaya çıkar. Yuvalanmış Döngüler Birleşimi dış referanslarını doğru günceller ve PrepRecompute
arayüz giriş metotları ile operatörleri kendi iç girişinde bilgilendirir . Bu kontrolün başında, iç taraf operatörleri CParamBounds:FNeedToReload
dış referansın son zamandan itibaren değişip değişmediğini görmek için özelliği okurlar . Örnek bir yığın izi aşağıda gösterilmiştir:
Yukarıda gösterilen alt ağaç, özellikle birleştirme işleminin kullanıldığı yerlerde CParamBounds:FNeedToReload
, dış referansın gerçekten değişip değişmediğine bakılmaksızın, her zaman yanlış döndüren ciltlemelerde bir şeyler ters gider (belki bir ByVal / ByRef / Kopyalama sorunu) .
Aynı alt ağaç bulunduğunda, ancak Bir Birlik Birliği veya Karma Birlik kullanıldığında, bu temel özellik her yinelemede doğru bir şekilde ayarlanır ve Tembel Endeks Biriktiricisi her seferinde uygun şekilde geri sarılır veya yeniden bağlanır. Bu arada, Ayırt Edici Sıralama ve Akış Toplamı suçsuzdur. Benim şüphem, Birleştirme ve Hash Birliği'nin önceki değerin bir kopyasını oluşturması, Oysa Birleştirme referans kullanıyor. Maalesef SQL Server kaynak koduna erişmeden bunu doğrulamak neredeyse imkansız.
Net sonuç, sorunlu plan şeklindeki Tembel Endeks Makarası'nın daima mevcut dış referansı görmüş olduğunu düşündüğü, çalışma masasını arayarak geri sardığı, genellikle hiçbir şey bulamadığı, yani o dış referans için hiçbir satırın geri dönmediğidir. Bir hata ayıklayıcısındaki yürütme adımlarında, makara, yalnızca RewindHelper
yöntemini çalıştırır ve asla ReloadHelper
yöntemini (yeniden yükleme = bu bağlamda yeniden bağlama). Bu, yürütme planında belirgindir, çünkü makara altındaki operatörlerin hepsinde 'İşlem Sayısı = 1' vardır.
İstisna, elbette, Tembel Endeks Makarası'nın ilk dış referansı için. Bu her zaman alt ağacı çalıştırır ve çalışma tablosunda bir sonuç satırını önbelleğe alır. İzleyen tüm yinelemeler, bir geri dönüşle sonuçlanır; geçerli yineleme, ilk kez etrafındaki dış referans için aynı değere sahip olduğunda yalnızca bir satır (tek önbelleğe alınmış satır) oluşturur.
Bu nedenle, Yuvalanmış Döngüler Birleşmesinin dış tarafında ayarlanan herhangi bir giriş için, sorgu, işlenen ilk satırın kopyaları olduğu kadar çok sayıda satır döndürür (elbette ilk satır için bir tane).
gösteri
Tablo ve örnek veriler:
CREATE TABLE #T1
(
pk integer IDENTITY NOT NULL,
c1 integer NOT NULL,
CONSTRAINT PK_T1
PRIMARY KEY CLUSTERED (pk)
);
GO
INSERT #T1 (c1)
VALUES
(1), (2), (3), (4), (5), (6),
(1), (2), (3), (4), (5), (6),
(1), (2), (3), (4), (5), (6);
Aşağıdaki (önemsiz) sorgu, Bir Birlik Birliği kullanarak her satır için (toplamda 18) iki doğru sayı üretir:
SELECT T1.c1, C.c1
FROM #T1 AS T1
CROSS APPLY
(
SELECT COUNT_BIG(*) AS c1
FROM
(
SELECT T1.c1
UNION
SELECT NULL
) AS U
) AS C;
Şimdi bir birleştirme zorlamak için bir sorgu ipucu eklersek:
SELECT T1.c1, C.c1
FROM #T1 AS T1
CROSS APPLY
(
SELECT COUNT_BIG(*) AS c1
FROM
(
SELECT T1.c1
UNION
SELECT NULL
) AS U
) AS C
OPTION (CONCAT UNION);
Uygulama planı sorunlu bir şekle sahiptir:
Ve sonuç şimdi yanlıştır, sadece üç satır:
Bu davranış garanti edilmese de, Kümelenmiş Dizin Taraması'ndan ilk satırın c1
değeri 1'dir. Bu değere sahip iki satır daha vardır, bu nedenle toplamda üç satır üretilir.
Şimdi veri tablosunu kesin ve 'ilk' satırının daha fazla kopyasıyla yükleyin:
TRUNCATE TABLE #T1;
INSERT #T1 (c1)
VALUES
(1), (2), (3), (4), (5), (6),
(1), (2), (3), (4), (5), (6),
(1), (1), (1), (1), (1), (1);
Şimdi birleştirme planı:
Ve belirtildiği gibi, c1 = 1
elbette ki , 8 satır üretilir :
Bu hata için bir Connect öğesi açtığınızı fark ediyorum ama bu gerçekten de, üretim etkisi olan sorunları bildirmenin yeri değil. Bu durumda, gerçekten Microsoft Destek ile iletişim kurmanız gerekir.
Bu yanlış sonuç hatası bir aşamada giderildi. Artık 2012'den itibaren benim için hiçbir SQL Server sürümünde çoğaltılmıyor. SQL Server 2008 R2 SP3-GDR sürüm 10.50.6560.0 (X64) üzerinde repro yapar.