2010-03-31 9 views
166

Quelques exemples pour montrer, juste Incase:Table multi-statement fonction de valeur vs table en ligne Valued Fonction

Table Inline Valued

CREATE FUNCTION MyNS.GetUnshippedOrders() 
RETURNS TABLE 
AS 
RETURN SELECT a.SaleId, a.CustomerID, b.Qty 
    FROM Sales.Sales a INNER JOIN Sales.SaleDetail b 
     ON a.SaleId = b.SaleId 
     INNER JOIN Production.Product c ON b.ProductID = c.ProductID 
    WHERE a.ShipDate IS NULL 
GO 

Tableau multi Déclaration Valued

CREATE FUNCTION MyNS.GetLastShipped(@CustomerID INT) 
RETURNS @CustomerOrder TABLE 
(SaleOrderID INT   NOT NULL, 
CustomerID  INT   NOT NULL, 
OrderDate  DATETIME NOT NULL, 
OrderQty  INT   NOT NULL) 
AS 
BEGIN 
    DECLARE @MaxDate DATETIME 

    SELECT @MaxDate = MAX(OrderDate) 
    FROM Sales.SalesOrderHeader 
    WHERE CustomerID = @CustomerID 

    INSERT @CustomerOrder 
    SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty 
    FROM Sales.SalesOrderHeader a INNER JOIN Sales.SalesOrderHeader b 
     ON a.SalesOrderID = b.SalesOrderID 
     INNER JOIN Production.Product c ON b.ProductID = c.ProductID 
    WHERE a.OrderDate = @MaxDate 
     AND a.CustomerID = @CustomerID 
    RETURN 
END 
GO 

Y a-t-il un avantage à utiliser un type (en ligne ou multi-instructions) par rapport à l'autre r? Y a-t-il des scénarios où l'un est meilleur que l'autre ou les différences sont-elles purement syntaxiques? Je me rends compte que les deux exemples de requêtes font des choses différentes, mais y a-t-il une raison pour que je les écris de cette façon?

La lecture à leur sujet et les avantages/différences n'ont pas vraiment été expliqués.

+0

également l'un des énormes avantages de la fonction en ligne est que vous pouvez sélectionner les colonnes ROWID (TIMESTAMP), alors que vous ne pouvez pas insérer des données TIMESTAMP à la table de retour fonction multistatement! – Artru

+3

Merci pour un excellent fil. J'ai beaucoup appris. Cependant, une chose à garder à l'esprit est quand ALTER une fonction qui était ITV à MSTV, le profileur pense que vous modifiez un ITV. Peu importe ce que vous faites pour obtenir la syntaxe correcte d'un point de vue MSTV, la recompilation échoue toujours, généralement autour de la première instruction après BEGIN. Le seul moyen de contourner cela était de supprimer l'ancienne fonction et de CREER la nouvelle en tant que MSTV. – Fandango68

Répondre

121

En recherchant le commentaire de Matt, j'ai révisé ma déclaration originale. Il a raison, il y aura une différence de performance entre une fonction de valeur de table en ligne (ITVF) et une fonction de valeur de table multi-instructions (MSTVF) même si elles exécutent toutes deux simplement une instruction SELECT. SQL Server traitera un ITVF comme un VIEW en ce qu'il calculera un plan d'exécution en utilisant les dernières statistiques sur les tables en question. Un MSTVF est équivalent à bourrer le contenu entier de votre instruction SELECT dans une variable de table, puis de se joindre à cela. Ainsi, le compilateur ne peut utiliser aucune statistique de table sur les tables du fichier MSTVF. Donc, toutes choses égales par ailleurs (ce qu'elles sont rarement), l'ITVF sera plus performant que le MSTVF. Dans mes tests, la différence de performance dans le temps d'achèvement était négligeable mais du point de vue des statistiques, c'était perceptible.

Dans votre cas, les deux fonctions ne sont pas fonctionnellement équivalentes. La fonction MSTV fait une requête supplémentaire chaque fois qu'elle est appelée et, surtout, des filtres sur l'identifiant du client. Dans une requête de grande taille, l'optimiseur ne serait pas en mesure de tirer parti d'autres types de jointures car il aurait besoin d'appeler la fonction pour chaque customerId transmis.Toutefois, si vous récrit votre fonction MSTV comme ceci:

CREATE FUNCTION MyNS.GetLastShipped() 
RETURNS @CustomerOrder TABLE 
    (
    SaleOrderID INT   NOT NULL, 
    CustomerID  INT   NOT NULL, 
    OrderDate  DATETIME NOT NULL, 
    OrderQty  INT   NOT NULL 
    ) 
AS 
BEGIN 
    INSERT @CustomerOrder 
    SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty 
    FROM Sales.SalesOrderHeader a 
     INNER JOIN Sales.SalesOrderHeader b 
      ON a.SalesOrderID = b.SalesOrderID 
     INNER JOIN Production.Product c 
      ON b.ProductID = c.ProductID 
    WHERE a.OrderDate = (
         Select Max(SH1.OrderDate) 
         FROM Sales.SalesOrderHeader As SH1 
         WHERE SH1.CustomerID = A.CustomerId 
         ) 
    RETURN 
END 
GO 

Dans une requête, l'optimiseur serait en mesure d'appeler cette fonction une fois et construire un meilleur plan d'exécution, mais il ne serait pas mieux que l'équivalent , ITVS non paramétré ou VIEW. Les ITVF devraient être préférés sur un MSTVF quand cela est possible parce que les types de données, la nullité et l'assemblage des colonnes dans la table alors que vous déclarez ces propriétés dans une fonction de table à plusieurs instructions et, surtout, vous obtiendrez de meilleurs plans d'exécution l'ITVF. Dans mon expérience, je n'ai pas trouvé beaucoup de circonstances où un ITVF était une meilleure option qu'une VUE, mais le kilométrage peut varier.

Merci à Matt.

Addition

Depuis que j'ai vu ce venir récemment, voici une excellente analyse faite par Wayne Sheffield comparer la différence de performance entre les fonctions table en ligne et fonctions Précieuses multi-déclaration.

His original blog post.

Copy on SQL Server Central

+37

Ce n'est tout simplement pas vrai - Les fonctions multi-instructions sont très souvent un très gros problème de performance car elles empêchent l'optimiseur de requêtes d'utiliser des statistiques. Si j'avais 1 $ pour chaque fois que j'ai vu la fonction multi-instructions utiliser un très mauvais choix de plan d'exécution (surtout parce qu'il estime généralement le nombre de lignes retournées à 1), j'en aurais assez pour acheter une petite voiture. –

+0

La meilleure explication que j'ai jamais trouvée est dans la première réponse, et le poste connexe: http://stackoverflow.com/questions/4109152/table-valued-function-killing-my-query-performance Ne manquez pas le document connexe, vous pouvez le lire rapidement, et c'est extrêmement intéressant. – JotaBe

+0

Y aura-t-il une mise à jour de cette réponse pour SQL Server 2017 ?: https://www.youtube.com/watch?time_continue=2&v=szTmo6rTUjM – Ralph

3

Vos exemples, je pense, répondent très bien à la question. La première fonction peut être effectuée en tant que sélection unique, et est une bonne raison d'utiliser le style en ligne. La deuxième pourrait probablement être faite en une seule instruction (en utilisant une sous-requête pour obtenir la date max), mais certains codeurs peuvent trouver plus facile à lire ou plus naturel de le faire dans plusieurs instructions comme vous l'avez fait. Certaines fonctions ne peuvent tout simplement pas être effectuées dans une seule instruction, et nécessitent donc la version multi-instructions. Je suggère d'utiliser le plus simple (en ligne) chaque fois que possible, et en utilisant des instructions multiples lorsque cela est nécessaire (évidemment) ou lorsque la préférence personnelle/la lisibilité rend wirth la frappe supplémentaire.

+0

Merci pour la réponse. Donc, fondamentalement, la multi-déclaration est seulement vraiment à utiliser lorsque la fonction est plus compliquée que ce qui est faisable dans une fonction inline, par souci de lisibilité? Y a-t-il des avantages de performance à multi-déclaration? – AndrewC

+0

Je ne sais pas, mais je ne le pense pas. Il est probablement préférable de laisser le serveur sql déterminer les optimisations que vous pourriez essayer de faire manuellement (en utilisant des variables, des tables temporaires, etc.). Bien que vous puissiez certainement faire des tests de performance pour prouver/réfuter cela dans des cas spécifiques. – Ray

+0

Merci beaucoup encore. Je peux regarder plus loin quand j'ai plus de temps! :) – AndrewC

-2

si vous allez faire une requête, vous pouvez joindre à votre fonction table Inline comme:

SELECT 
    a.*,b.* 
    FROM AAAA a 
     INNER JOIN MyNS.GetUnshippedOrders() b ON a.z=b.z 

il engagera peu de frais généraux et fonctionnent très bien.

si vous essayez d'utiliser votre tableau multi déclaration Valued dans une requête similaire, vous aurez des problèmes de performance:

SELECT 
    x.a,x.b,x.c,(SELECT OrderQty FROM MyNS.GetLastShipped(x.CustomerID)) AS Qty 
    FROM xxxx x 

parce que vous exécutez la fonction 1 fois pour chaque ligne retournée, comme le jeu de résultats devient grand, il va courir plus lentement et plus lentement.

+0

Ah, donc vous diriez que l'inline est bien meilleur en termes de performance? – AndrewC

+1

Non, ils renvoient tous deux une table, ce qui rend votre deuxième SQL invalide lorsque vous essayez de placer une table dans une colonne. – cjk

+1

@ck, j'ai mis à jour la requête que vous avez commentée. les paramètres de la fonction utilisée dans la seconde fonction lui permettent d'être utilisée comme sous-requête, ce qui entraînera des performances moins bonnes. –

24

interne, SQL Server traite une table en ligne fonction d'une valeur comme il l'aurait vue et traite une fonction table multi-déclaration semblable à la façon dont il serait une procédure stockée. Lorsqu'une fonction de table en ligne est utilisée dans le cadre d'une requête externe, le processeur de requête développe la définition UDF et génère un plan d'exécution qui accède aux objets sous-jacents en utilisant les index de ces objets.

Pour une fonction de valeur de table multi-instructions, un plan d'exécution est créé pour la fonction elle-même et stocké dans le cache du plan d'exécution (une fois la fonction exécutée la première fois). Si des fonctions de valeur de table multi-instructions sont utilisées dans le cadre de requêtes plus volumineuses, l'optimiseur ne sait pas ce que renvoie la fonction, et fait donc certaines hypothèses standard - en effet, il suppose que la fonction retourne une seule ligne et que la fonction sera accessible en utilisant un balayage de table contre une table avec une seule ligne.

Lorsque les fonctions de valeur de tableau à instructions multiples peuvent mal fonctionner, c'est lorsqu'elles renvoient un grand nombre de lignes et qu'elles sont jointes dans des requêtes externes. Les problèmes de performance sont principalement dus au fait que l'optimiseur produira un plan en supposant qu'une seule ligne est retournée, ce qui ne sera pas nécessairement le plan le plus approprié. En règle générale, nous avons constaté que, si possible, les fonctions de table en ligne doivent être utilisées de préférence aux fonctions multi-instructions (lorsque la fonction UDF sera utilisée dans le cadre d'une requête externe) en raison de ces problèmes de performances potentiels. .

+2

Bien qu'il puisse traiter des fonctions de table à plusieurs instructions similaires à une procédure stockée, un Une procédure stockée fonctionnellement identique est beaucoup plus rapide qu'une fonction de valeur table pour les grands ensembles de données. Je colle avec des procs stockés au-dessus des fonctions évaluées de table multi-instruction. – Kekoa

+5

Sauf si vous avez besoin de joindre ces résultats à une autre requête. –

+0

pourquoi ne pas utiliser les deux? Processus stocké qui renvoie le résultat d'une fonction table à plusieurs instructions. Le meilleur des deux mondes. – Robino

11

Il existe une autre différence. Une fonction de table en ligne peut être insérée, mise à jour et supprimée - comme une vue. Des restrictions similaires s'appliquent: impossible de mettre à jour des fonctions à l'aide d'agrégats, de mettre à jour des colonnes calculées, etc.

Questions connexes