9

Ich versuche, eine TVF zweimal mit verschiedenen Parametern in der gleichen Abfrage aufzurufen, aber aus irgendeinem Grund, wenn ich die Ergebnisse zusammen, eines der Ergebnisse Art maskiert den anderen. Ich habe mein Problem auf dieses kleine Beispiel reduziert:Aufruf von mehreren Anweisungen TVF mit verschiedenen Parametern in separaten CTEs mit falschen Ergebnissen

Nehmen Sie den Inline-TVF:

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] 
) 

und eine äquivalente mehrzeiligen Funktion:

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 

Wenn ich diese Abfrage ausführen:

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; 

Die Ergebnisse sind wie erwartet:

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

aber wenn ich die mehrzeilige Version der Funktion:

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; 

die Ergebnisse sind falsch - CTE2 die gleichen Werte wie CTE1 zeigt:

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

Zusätzlich sehe ich nur dieses Verhalten wenn die GROUP BY vorhanden ist. Ohne es sind die Ergebnisse in Ordnung.

Seltsam, wenn ich eine andere Säule mit dem zweiten CTE hinzufügen, ändert sich die Ergebnisse:

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; 

ergibt

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 

Es erscheint die zusätzliche Spalte eine Spalte in der TVF Tabelle verweisen muss - ein konstanter Wert dort ändert die Ergebnisse nicht.

Was geht hier vor? Soll ein Multiline-TVF nicht mehr als einmal pro Abfrage aufgerufen werden?

Ich habe das 2008 R2 auf SQL Server getestet und 2012

+0

Bin vorbei 3 bis 'fnTestErrorMultiline' in der zweiten CTE noch kann ich Ergebnisse sehen. http://sqlfiddle.com/#!6/e0395/13. Es ist komisch –

+0

@ MM93 das ist komisch! Das scheint zu implizieren, dass die Funktion nur einmal ausgeführt wird, da das zweite CTE keine Zeilen zum Verknüpfen hätte. Wenn ich den Abfrageplan anschaue, wird "Table Valued Function" zweimal angezeigt, so dass ich wirklich nicht weiß, was passiert. – David

+0

Wenn ich die Reihenfolge (zB) durch '3' nach' fnTestErrorMultiline' im ersten CTE ändere, kann ich keine Ergebnisse sehen –

Antwort

7

Dies ist ein bekannter Fehler in SQL Server, wo es falsch, die Ergebnisse für eine Instanz des TVF sie spulen kann und Wiedergabe für den anderen (trotz der Tatsache, dass der andere verschiedene Parameter hat und unterschiedliche Ergebnisse liefert).

Der Fehler existiert seit einiger Zeit, aber die jüngsten Änderungen am Kardinalitätsschätzer bedeuten, dass 2014+ dieses Problem noch häufiger auftritt.

Siehe Artikel verbinden ..

Hinweis: Der Ausführungsplan sieht wie folgt aus.

enter image description here

Es verwendet eine Common Subexpression Spool Alle drei markierten Spulen sind in der Tat das gleiche Objekt, in dem gelben Bediener die Zeilen eingefügt werden, und dann werden sie in den grünen Betreiber wiederholt.

Hinzufügen

OPTION (QUERYRULEOFF GenGbApplySimple, QUERYRULEOFF BuildGbApply) 

vermeidet das Problem und gibt einen anderen Plan mit korrekten Ergebnissen, aber das ist nicht etwas, was ich in der Produktion verwenden würde.

enter image description here

+0

Würden Sie diese Abfrageoptionen aus Leistungsgründen oder aus einem anderen Grund vermeiden? Ich kann meine Abfrage mit diesen Optionen vergleichen, anstatt die Abfrage anders zu erstellen (Tabellenvariable, um die Ergebnisse oder etwas zu speichern), um zu sehen, wie sie funktioniert, es sei denn, die Optionen haben einen anderen negativen Nebeneffekt. Diese spezielle Abfrage wird nicht oft ausgeführt, daher kann ich einen Leistungseinbruch tolerieren, wenn das alles ist. – David

+1

Und wow, ich habe mir das erste Verbindungsproblem, das du verlinkt hast, genauer angeschaut. Unsere Beispiele waren nahezu identisch. – David

+1

@David sie sind undokumentiert, nicht unterstützt etc. in einer Tabelle Variable zu funktionieren würde funktionieren. Oder einfach kopieren Sie die Funktionsdefinition und haben zwei identische Versionen mit unterschiedlichen Namen, so dass Sie nur vermeiden, die Self-Join würde auch funktionieren. –

Verwandte Themen