9

Aynı sorguda farklı parametrelerle iki kez bir TVF'yi aramaya çalışıyorum, ancak bazı nedenlerle, sonuçlara birlikte katıldığımda, sonuçlardan biri Öbür maskeler diğeri. Ben bu küçük örneğe sorunumu aşağı düşürdük:Hatalı sonuçlar gösteren ayrı CTE'lerde farklı parametreler içeren çok bildirimli TVF çağrısı

CREATE FUNCTION dbo.fnTestErrorInline(@Test INT) 
RETURNS TABLE 
AS 
RETURN 
(
    SELECT ID, Val 
    FROM (VALUES 
     (1, 1, 10), 
     (1, 2, 20), 
     (1, 3, 30), 
     (1, 4, 40), 
     (2, 1, 50), 
     (2, 2, 60), 
     (2, 3, 70), 
     (2, 4, 80) 
    ) t(Test, ID, Val) 
    WHERE [email protected] 
) 

ve bir eşdeğer satırlı fonksiyonu:

bu satır içi TVF al

CREATE FUNCTION dbo.fnTestErrorMultiline(@Test INT) 
RETURNS @tbl TABLE (
    ID INT NOT NULL, 
    Val INT NOT NULL 
) 
AS 
BEGIN 
    IF @Test=1 
    INSERT INTO @tbl (ID, Val) VALUES 
    (1, 10), 
    (2, 20), 
    (3, 30), 
    (4, 40); 

    IF @Test=2 
    INSERT INTO @tbl (ID, Val) VALUES 
    (1, 50), 
    (2, 60), 
    (3, 70), 
    (4, 80); 

    RETURN 
END 

ben bu sorguyu çalıştırırsanız:

WITH cte1 AS (
    SELECT ID, SUM(Val) AS Total 
    FROM dbo.fnTestErrorInline(1) 
    GROUP BY ID 
), cte2 AS (
    SELECT ID, SUM(Val) AS Total 
    FROM dbo.fnTestErrorInline(2) 
    GROUP BY ID 
) 
SELECT * 
FROM cte1 c1 
INNER JOIN cte2 c2 ON c1.ID=c2.ID; 

sonuçlar beklendiği gibi olur:

ID Total ID Total 
1 10 1 50 
2 20 2 60 
3 30 3 70 
4 40 4 80 

ama fonksiyonun satırlı sürümünü kullandığınızda:

WITH cte1 AS (
    SELECT ID, SUM(Val) AS Total 
    FROM dbo.fnTestErrorMultiline(1) 
    GROUP BY ID 
), cte2 AS (
    SELECT ID, SUM(Val) AS Total 
    FROM dbo.fnTestErrorMultiline(2) 
    GROUP BY ID 
) 
SELECT * 
FROM cte1 c1 
INNER JOIN cte2 c2 ON c1.ID=c2.ID; 

sonuç yanlıştır - cte2 cte1 aynı değerleri gösterir: Ayrıca

ID Total ID Total 
1 10 1 10 
2 20 2 20 
3 30 3 30 
4 40 4 40 

, sadece bu davranışı görmek GROUP BY mevcut olduğunda. Bu olmadan, sonuçlar iyi.

WITH cte1 AS (
    SELECT ID, SUM(Val) AS Total 
    FROM dbo.fnTestErrorMultiline(1) 
    GROUP BY ID 
), cte2 AS (
    SELECT ID, SUM(Val) AS Total, SUM(Val+0) AS why 
    FROM dbo.fnTestErrorMultiline(2) 
    GROUP BY ID 
) 
SELECT * 
FROM cte1 c1 
INNER JOIN cte2 c2 ON c1.ID=c2.ID; 

Ekstra sütun TVF tablodaki bir sütuna başvuru gerekiyor görünür

ID Total ID Total why 
1 50 1 50 50 
2 60 2 60 60 
3 70 3 70 70 
4 80 4 80 80 

verir: Ben ikinci CTE için başka bir sütun eklerseniz

Garip bir şekilde, bu sonuçları değiştirir - Sabit bir değer, sonuçları değiştirmez.

Neler oluyor burda? Çok satırlı bir TVF'i sorgu başına birden çok kez çağırmamanız mı gerekiyor?

ben SQL Server 2008 R2 ve bu test ettik 2012

+0

hala sonuçları görebilirsiniz ikinci CTE fnTestErrorMultiline' '3 ila geçirerek. http://sqlfiddle.com/#!6/e0395/13. Garip olan –

+0

@ MM93 tuhaf! Bu, ikinci CTE'nin katılacağı herhangi bir satır olmayacağından, fonksiyonun sadece bir kez çalıştığını ima ettiği görülüyor. Sorgu planına bakıldığında, 'Tablo Değerli Fonksiyonu' iki kez göründüğünden, gerçekten ne olduğunu bilmiyorum – David

+0

Siparişi değiştirirseniz (yani) ilk CTE'de '3''ü' fnTestErrorMultiline'yi geçersem sonuçları göremiyorum –

cevap

7

Bu yanlış TVF biri örneğin sonuçlarını biriktirmek ve diğer onları yeniden oynatabilir SQL Server bilinen bir hata (rağmen diğerinin farklı parametreleri olması ve farklı sonuçlar döndürmesi).

Hata bir süredir mevcuttu, ancak kardinalite tahmincisindeki son değişiklikler, 2014'te bu sorunun daha da artması anlamına geliyor.

öğeleri bağlamak Bkz ..

  • Unexpected results in SQL
  • Count wrong when function_1 inner joined to function_1

      .
    0BNot: Yürütme planı aşağıdaki gibi görünür.

    enter image description here

    bir Common Subexpression Spool tüm üç sıra yerleştirilir sarı operatörü, bobinler, aslında aynı nesne vurgulanmış ve daha sonra yeşil operatörler çalınır kullanır.

    OPTION (QUERYRULEOFF GenGbApplySimple, QUERYRULEOFF BuildGbApply) 
    

    ekleme

    sorunu önler ve doğru sonuçlarla farklı bir planı verir ama bu üretimde kullanacağı bir şey değildir.

    enter image description here

  • +0

    Performans nedenleriyle veya başka bir nedenden dolayı bu sorgu seçeneklerinden kaçınmak ister misiniz? Sorgularımın başka bir negatif yan etkiye sahip olmadıkça nasıl çalıştığını görmek için sorguyu farklı şekilde oluşturma (sonuçları veya bir şeyleri tutmak için tablo değişkenini) ile karşılaştırabilirim. Bu belirli sorgu çok sık çalıştırılmıyor, bu yüzden bir performans isabetini tüm bunlara rağmen tahammül edebilirim. – David

    +1

    Ve vay, bağlandığınız ilk bağlantı sorununa daha yakından baktım. Örneklerimiz neredeyse aynıydı. – David

    +1

    @David belgelenmemiş, desteklenmeyen vb. Ya da sadece işlev tanımını kopyalayın ve farklı isimlere sahip iki özdeş versiyona sahip olursunuz; –