web-dev-qa-db-ger.com

Tabellenwertfunktion Beendet meine Abfrageleistung

Ich hatte heute eine schreckliche Zeit, als ich versuchte, eine Abfrage so auszuführen, wie ich es erwartet hatte. Ich musste eine geringfügige Änderung an einer Tabellenwertfunktion vornehmen, die gestern in der Abfrage enthalten ist. Diese Änderung hatte einen großen Einfluss auf die Leistung der Abfrage. Nachdem ich den Ausführungsplan ausgewertet und die Statistiken IO und Time angesehen hatte, stellte ich fest, dass ich, da ich die Funktion so geändert hatte, dass sie eine Tabellenvariable anstelle einer Ergebnismenge zurückgibt, einen vollständigen Scan einer der abgefragten Tabellen durchführt .

Meine Frage ist, warum die Tabelle (TableVariable) statt nur einer Auswahl/Ergebnismenge eine solche große Änderung des Plans zur Folge haben würde.

Gestockt ....

21
scarpacci

Wenn Sie eine Tabellenvariable zurückgeben, wird sie zu einer Multi-Statement-Tabelle - Wertfunktion und kann aufgrund der Tatsache, dass sie wie eine Tabelle behandelt wird, die Leistung beeinträchtigen, es sei denn, es sind keine Statistiken für SQL Server verfügbar, die eine gute Ausführung ermöglichen plan on - so schätzt es die Funktion als Rückgabe einer sehr kleinen Anzahl von Zeilen. Wenn eine größere Anzahl von Zeilen zurückgegeben wird, ist der erstellte Plan möglicherweise nicht optimal.

Wenn Sie nur ein SELECT zurückgeben, wird dies zu einer Inline-Tabellenwertfunktion -, die Sie eher als eine Ansicht betrachten. In diesem Fall werden die tatsächlich zugrunde liegenden Tabellen in die Hauptabfrage eingefügt, und basierend auf den richtigen Statistiken kann ein besserer Ausführungsplan erstellt werden. Sie werden feststellen, dass der Ausführungsplan in diesem Fall die Funktion überhaupt NICHT erwähnt, da er die Funktion im Wesentlichen nur in die Hauptabfrage einfügt.

Es gibt eine gute Referenz auf MSDN von CSS SQL Server Engineers, einschließlich (Zitat):

Wenn Sie TVF mit mehreren Anweisungen verwenden, wird Jedoch wie eine andere Tabelle Behandelt. Da keine - Statistik verfügbar ist, hat SQL Server , Um einige Annahmen zu treffen, und in Eine allgemeine Schätzung. Wenn Ihr TVF nur wenige Zeilen zurückgibt, ist In Ordnung. Wenn Sie jedoch beabsichtigen, Die TVF mit Tausenden von Zeilen zu füllen, und wenn diese TVF mit Anderen Tabellen verbunden ist, kann ein ineffizienter Plan Aus einer Kardinalitätsschätzung resultieren .

55
AdaTheDev

Dies ist darauf zurückzuführen, dass eine UDF mit mehreren Anweisungstabellenwerten nicht inline mit dem Rest des SQL-Statements verarbeitet werden kann, in dem sie verwendet wird, und daher nicht Teil des Anweisungscacheplans sein kann Das SQL wird immer wieder in für jede Zeile in der endgültigen Ergebnismenge verwendet , die von der Abfrage generiert wird. 

Eine Inline Table-Value-UDF, otoh, wird zusammen mit der verwendeten SQL verarbeitet und kompiliert. Sie wird daher zu part des Cache-Plans und wird nur verarbeitet und kompiliert einmal , egal wie viele Zeilen Sie erzeugen. 

5
Charles Bretana

Wirklich unmöglich, ohne weitere Informationen definitiv zu antworten. Allerdings nehme ich gerne verrückte Stiche im Dunkeln. . .

Tabellenvariablen können nicht von der Engine optimiert werden. Die Engine "nimmt immer an", dass die Tabellenvariable nur eine Zeile enthält, wenn sie einen Ausführungsplan generiert. Das ist ein Grund, warum Sie eine merkwürdige Leistung sehen können.

3
Phil Sandler

Auf dem SQL Server 2014 konnten wir unser Problem lösen, indem Sie Tabellenwertfunktionsdaten in die temporäre Tabelle einfügen und dann dazu beitreten. Anstatt einen Join direkt mit der Tabellenwertfunktion durchzuführen. 

Dies hat unsere Ausführungszeit von 2 Minuten auf 4 Sekunden verbessert. 

Hier ein Beispiel, das für unser Team funktioniert hat:

- SLOW QUERY (2 min):

DECLARE @id INT = 1;

SELECT * 
FROM [data].[someTable] T
INNER JOIN [data].[tableValueFunction](@id) TVF ON TVF.id = T.id;

- SCHNELLE ANFRAGE (4 s):

DECLARE @id INT = 1;

SELECT * 
INTO #tableValueFunction
FROM [data].[tableValueFunction](@id) TVF

SELECT * 
FROM [data].[someTable] T
INNER JOIN #tableValueFunction TVF ON TVF.id = T.id;
1
Nanuz

Wenn Sie eine UDF mit mehreren Anweisungen für Tabellenwerte verwenden, wird diese UDF vollständig ausgeführt, bevor ihre Ergebnisse vom Aufrufer verwendet werden können. Mit einer Inline-Tabellenwert-UDF erweitert der SQL Server die UDF im Prinzip genauso wie macro extension in die aufrufende Abfrage. Dies hat unter anderem folgende Auswirkungen:

  • Die WHERE-Klausel der aufrufenden Abfrage kann direkt in eine UDF mit Inline-Tabellenwerten interpoliert werden, nicht jedoch in eine UDF mit mehreren Anweisungen. Wenn Ihre UDF mit Tabellenwert viele Zeilen generiert, die von der WHERE-Klausel der aufrufenden Abfrage herausgefiltert werden, kann das Abfrageoptimierungsprogramm die WHERE-Klausel direkt in eine Inline-UDF mit Tabellenwert anwenden, nicht jedoch in eine UDF mit mehreren Anweisungen .
  • Eine Inline-Tabellenwert-UDF verhält sich wie eine parametrisierte VIEW, wenn der SQL Server über ein solches Konzept verfügt, während sich eine UDF mit mehreren Anweisungen und Tabellenwerten so verhält, als würden Sie sie füllen und dann eine Tabellenvariable in Ihrer Abfrage verwenden.

Wenn Ihre UDF viele Zeilen zurückgibt und von einer Tabelle unterstützt wird, kann ich mir vorstellen, dass der Tabellenscan hierher kommt. Fügen Sie Ihrer UDF entweder weitere Parameter hinzu, damit der Aufrufer die Ergebnisgröße einschränken kann, oder versuchen Sie, diese mit Hilfe von Freunden wie UNION et al. Ich würde vermeiden, UDFs mit mehreren Tabellenwerten zu verwenden, es sei denn, die Ergebnisgröße ist bekanntermaßen nur wenige Zeilen und , und es ist schwierig, die erforderlichen Ergebnisse mit einer satzbasierten Logik zu erzeugen.

0
binki