Quelques exemples à montrer, au cas où:
Table en ligne valorisée
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 d'instructions multiples évalué
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-instruction) par rapport à l'autre? Y a-t-il certains 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 laquelle je les écrirais de cette façon?
Leur lecture et les avantages / différences n'ont pas vraiment été expliqués.
Réponses:
En recherchant le commentaire de Matt, j'ai révisé ma déclaration d'origine. Il a raison, il y aura une différence de performances entre une fonction de valeur de table en ligne (ITVF) et une fonction de valeur de table à instructions multiples (MSTVF) même si elles exécutent toutes deux simplement une instruction SELECT. SQL Server traitera un ITVF un peu 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 équivaut à bourrer tout le contenu de votre instruction SELECT dans une variable de table, puis à s'y joindre. Par conséquent, le compilateur ne peut utiliser aucune statistique de table sur les tables du MSTVF. Donc, toutes choses étant égales par ailleurs (ce qu'elles sont rarement), l'ITVF fonctionnera mieux que le MSTVF. Dans mes tests, la différence de performance dans le temps de réalisation était négligeable mais d'un point de vue statistique, elle était notable.Dans votre cas, les deux fonctions ne sont pas fonctionnellement équivalentes. La fonction MSTV effectue une requête supplémentaire chaque fois qu'elle est appelée et, plus important encore, filtre l'ID client. Dans une requête volumineuse, l'optimiseur ne pourrait pas tirer parti d'autres types de jointures car il aurait besoin d'appeler la fonction pour chaque customerId transmis. Cependant, si vous avez réécrit votre fonction MSTV comme ceci:
Dans une requête, l'optimiseur serait en mesure d'appeler cette fonction une fois et de créer un meilleur plan d'exécution, mais ce ne serait toujours pas mieux qu'un ITVS équivalent ou non paramétré
VIEW
.Les ITVF doivent être préférés aux MSTVF lorsque cela est possible car les types de données, la nullité et le classement des colonnes de la table tandis que vous déclarez ces propriétés dans une fonction de valeur de table à instructions multiples et, surtout, vous obtiendrez de meilleurs plans d'exécution de l'ITVF. D'après mon expérience, je n'ai pas trouvé de nombreuses circonstances où un ITVF était une meilleure option qu'un VIEW mais le kilométrage peut varier.
Merci à Matt.
Une addition
Depuis que j'ai vu cela se produire récemment, voici une excellente analyse effectuée par Wayne Sheffield comparant la différence de performance entre les fonctions Inline Table Valued et les fonctions multi-instructions.
Son article de blog original.
Copier sur SQL Server Central
la source
En interne, SQL Server traite une fonction de valeur de table en ligne un peu comme une vue et traite une fonction de valeur de table à instructions multiples de la même manière qu'une procédure stockée.
Lorsqu'une fonction table en ligne est utilisée dans le cadre d'une requête externe, le processeur de requêtes développe la définition UDF et génère un plan d'exécution qui accède aux objets sous-jacents, à l'aide des index sur ces objets.
Pour une fonction de valeur de table à instructions multiples, 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 que la fonction a été exécutée la première fois). Si des fonctions de valeur de table à instructions multiples sont utilisées dans le cadre de requêtes plus importantes, l'optimiseur ne sait pas ce que la fonction retourne et fait donc certaines hypothèses standard - en fait, il suppose que la fonction renverra une seule ligne et que les retours de la fonction sera accessible en utilisant une analyse de table par rapport à une table avec une seule ligne.
Les fonctions de valeur de table à instructions multiples peuvent mal fonctionner lorsqu'elles renvoient un grand nombre de lignes et sont jointes dans les requêtes externes. Les problèmes de performances 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, dans la mesure du possible, les fonctions de valeur de table en ligne devraient être utilisées de préférence à celles à instructions multiples (lorsque l'UDF sera utilisé dans le cadre d'une requête externe) en raison de ces problèmes de performances potentiels.
la source
Il y a une autre différence. Une fonction de valeur de table en ligne peut être insérée, mise à jour et supprimée de - comme une vue. Des restrictions similaires s'appliquent - ne peuvent pas mettre à jour les fonctions à l'aide d'agrégats, ne peuvent pas mettre à jour les colonnes calculées, etc.
la source
Vos exemples, je pense, répondent très bien à la question. La première fonction peut être effectuée en une seule sélection et est une bonne raison d'utiliser le style en ligne. La seconde pourrait probablement être faite comme une seule instruction (en utilisant une sous-requête pour obtenir la date maximale), 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 exécutées en une seule instruction, et nécessitent donc la version multi-instructions.
Je suggère d'utiliser le plus simple (en ligne) dans la mesure du possible, et d'utiliser des instructions multiples lorsque cela est nécessaire (évidemment) ou lorsque la préférence / lisibilité personnelle rend la frappe supplémentaire.
la source
regardez Comparaison des fonctions de table en ligne et multi-instructions, vous pouvez trouver de bonnes descriptions et des références de performance
la source
Je n'ai pas testé cela, mais une fonction multi-instructions met en cache l'ensemble de résultats. Il peut y avoir des cas où il se passe trop de choses pour que l’optimiseur intègre la fonction. Par exemple, supposons que vous ayez une fonction qui renvoie un résultat provenant de différentes bases de données en fonction de ce que vous passez en tant que «numéro d'entreprise». Normalement, vous pouvez créer une vue avec une union, puis filtrer par numéro d'entreprise, mais j'ai trouvé que parfois le serveur sql retire la totalité de l'union et n'est pas assez intelligent pour appeler la sélection. Une fonction de table peut avoir une logique pour choisir la source.
la source
Un autre cas pour utiliser une fonction multi-ligne serait de contourner le serveur SQL de pousser vers le bas la clause where.
Par exemple, j'ai une table avec des noms de table et certains noms de table sont formatés comme C05_2019 et C12_2018 et toutes les tables formatées de cette façon ont le même schéma. Je voulais fusionner toutes ces données dans une table et analyser 05 et 12 dans une colonne CompNo et 2018,2019 dans une colonne année. Cependant, il existe d'autres tables comme ACA_StupidTable que je ne peux pas extraire CompNo et CompYr et obtiendrais une erreur de conversion si j'essayais. Donc, ma requête était en deux parties, une requête interne qui n'a renvoyé que des tables formatées comme 'C_______', puis la requête externe a fait une conversion de sous-chaîne et d'int. c'est-à-dire Cast (Substring (2, 2) as int) as CompNo. Tout semble bon, sauf que le serveur SQL a décidé de mettre ma fonction Cast avant le filtrage des résultats et j'ai donc une erreur de conversion de brouillage d'esprit. Une fonction de table d'instructions multiples peut empêcher cela de se produire,
la source
Peut-être d'une manière très condensée. ITVF (inline TVF): plus si vous êtes une personne DB, c'est une sorte de vue paramétrée, prenez une seule SELECT
MTVF (Multi-statement TVF): développeur, crée et charge une variable de table.
la source
si vous allez faire une requête, vous pouvez joindre à votre fonction Inline Table Valued comme:
cela entraînera peu de frais généraux et fonctionnera bien.
si vous essayez d'utiliser votre table d'instructions multiples évaluée dans une requête similaire, vous aurez des problèmes de performances:
parce que vous exécuterez la fonction 1 fois pour chaque ligne renvoyée, lorsque le jeu de résultats devient volumineux, il s'exécutera de plus en plus lentement.
la source