web-dev-qa-db-fra.com

Table Valeur Fonction Killing My Performance Query

Aujourd'hui, je passais un temps horrible à essayer d'obtenir une requête qui se déroulerait comme prévu. J'ai dû apporter un léger changement à une fonction table évaluée qui vit dans la requête hier et ce changement a eu un impact considérable sur les performances de la requête. Après avoir évalué le plan d'exécution et examiné les statistiques IO et Time, j'ai constaté que, parce que j'avais modifié la fonction pour renvoyer une variable de table au lieu d'un ensemble de résultats, elle effectuait une analyse complète de l'une des tables interrogées. .

Ma question est la suivante: pourquoi l’avoir renvoyé la table (TableVariable) au lieu d’un ensemble Sélection/Résultat causerait-il un changement aussi important dans le plan?

Stumped ....

21
scarpacci

Le renvoi d'une variable de table en fera une fonction de table à plusieurs instructions et peut être dommageable pour les performances car elle est traitée comme une table, à l'exception de l'absence de statistiques permettant à SQL Server de fonder une bonne exécution. plan - la fonction sera donc considérée comme renvoyant un très petit nombre de lignes. S'il renvoie un plus grand nombre de lignes, le plan généré peut donc être beaucoup moins qu'optimal.

Tandis que renvoyer juste un SELECT en fait une fonction de la table inline - le considère plutôt comme une vue. Dans ce cas, les tables sous-jacentes réelles sont importées dans la requête principale et un meilleur plan d'exécution peut être généré sur la base de statistiques appropriées. Vous remarquerez que dans ce cas, le plan d'exécution NE mentionnera PAS la fonction, car il ne fait que la fusionner avec la requête principale.

Il existe une excellente référence sur MSDN par CSS SQL Server Engineers, y compris (quote):

Mais si vous utilisez TVF à déclarations multiples, , Il est traité comme un autre Tableau. Comme il n'y a pas de statistiques disponibles pour , SQL Server doit Pour émettre certaines hypothèses et, dans , Fournir une estimation basse. Si votre TVF ne renvoie que quelques lignes, tout ira bien. Mais si vous avez l'intention de Peupler la TVF avec des milliers de Lignes et si cette TVF est associée à Autres tables, un plan inefficace peut Résulter d'une faible estimation de la cardinalité .

55
AdaTheDev

Cela est dû au fait qu’une UDF à plusieurs instructions ne peut pas être traitée en ligne avec le reste de la déclaration SQL dans laquelle elle est utilisée, et ne peut donc pas faire partie du plan de cache des instructions. Cela signifie qu’elle doit être compilée séparément du reste de la déclaration. le SQL dans lequel il est utilisé, encore et encore,pour chaque ligne du résultat finalgénéré par la requête. 

Un Inline Table UDF, otoh, est traité et compilé avec le SQL dans lequel il est utilisé. Il devient doncpartiedu plan de cache et est uniquement traité. et compiléune fois, quel que soit le nombre de lignes que vous générez. 

5
Charles Bretana

Vraiment impossible de répondre définitivement sans plus d'informations. Cependant, depuis que j'aime prendre des coups de fou dans le noir. . .

Les variables de table ne peuvent pas être optimisées par le moteur - le moteur "suppose" toujours que la variable de table ne contient qu'une seule ligne lorsqu'elle génère un plan d'exécution. C'est une des raisons pour lesquelles vous pourriez voir des performances étranges.

3
Phil Sandler

Sur SQL Server 2014, nous avons pu résoudre notre problème en insérant des données de fonction de valeur de table dans une table temporaire, puis en les associant. Au lieu de faire une jointure directement à la fonction de valeur de table. 

Cela a amélioré notre temps d'exécution de 2 min à 4 secondes. 

Voici un exemple qui a fonctionné pour notre équipe:

--LOW QUERY (2 min):

DECLARE @id INT = 1;

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

- RECHERCHE RAPIDE (4 sec):

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

Lorsque vous utilisez un fichier UDF à valeurs de tableau comportant plusieurs instructions, ce fichier est exécuté avant que ses résultats ne puissent être utilisés par l'appelant. Avec une fonction UDF de table intégrée, SQL Server étend essentiellement la fonction UDF à la requête appelante, à l’instar de macro expansion . Cela a, entre autres, les implications suivantes:

  • La clause WHERE de la requête appelante peut être interpolée directement dans une FDU en table intégrée, mais pas dans une FDU à instructions multiples. Ainsi, si votre fichier UDF à valeur de table génère un grand nombre de lignes qui seraient filtrées par la clause WHERE de la requête appelante, l'optimiseur de requête peut appliquer la clause WHERE directement dans un fichier UDF à valeur table inline mais pas dans un fichier UDF à instructions multiples. .
  • Une fonction UDF de table incorporée se comporte comme le ferait une variable VIEW paramétrée si SQL Server disposait d'un tel concept, alors qu'une fonction UDF de table à instructions multiples se comporterait comme si vous aviez rempli et utilisé une variable de table dans votre requête.

Si votre UDF retourne plusieurs lignes et est supportée par une table, j'imagine que cela pourrait être l'origine de l'analyse de table. Ajoutez d’autres paramètres à votre fichier UDF pour permettre à l’appelant de limiter la taille de son résultat ou essayez de le reformuler en tant que fichier UDF avec une table intégrée à l’aide d’amis tels que UNION et al. J'éviterais à tout prix d'éviter les fonctions définies par l'utilisateur à plusieurs tables d'instructions, sauf si la taille du résultat n'est que de quelques lignes et , il est difficile de produire les résultats requis avec une logique basée sur les ensembles.

0
binki