Ami ou ennemi? Les vues SQL Server ont fait l'objet de débats houleux lorsque j'étais dans ma première année d'utilisation de SQL Server. Ils ont dit que c'était mauvais parce que c'était lent. Mais qu'en est-il aujourd'hui ?
Êtes-vous sur le même bateau que moi il y a de nombreuses années ? Ensuite, rejoignez-moi dans ce voyage pour découvrir le vrai problème des vues SQL afin que vous puissiez les écrire le plus rapidement possible.
Les vues SQL sont des tables virtuelles. Les enregistrements d'une vue sont le résultat d'une requête à l'intérieur de celle-ci. Chaque fois que les tables de base utilisées dans la vue sont mises à jour, la vue est également mise à jour. Vous pouvez également INSÉRER, METTRE À JOUR et SUPPRIMER des enregistrements dans une vue sous forme de table dans certains cas. Même si je n'ai pas essayé moi-même.
Comme pour une table, vous pouvez CREATE, ALTER ou DROP une vue. Vous pouvez même créer un index, avec certaines restrictions.
Notez que j'ai utilisé SQL Server 2019 dans les exemples de codes.
1. Connaître l'utilisation correcte et incorrecte des vues SQL
Tout d'abord, les bases.
A quoi servent les vues SQL ?
C'est crucial. Si vous l'utilisez comme un marteau sur un tournevis, oubliez les vues SQL plus rapides. Rappelons tout d'abord le bon usage :
- Pour cibler, simplifier et personnaliser la perception que chaque utilisateur a de la base de données.
- Pour permettre aux utilisateurs d'accéder aux seules informations dont ils ont besoin pour des raisons de sécurité.
- Pour fournir une rétrocompatibilité à une ancienne table ou à un ancien schéma afin de ne pas casser les applications dépendantes. Il est temporaire jusqu'à ce que toutes les modifications nécessaires soient effectuées.
- Pour partitionner les données provenant de différents serveurs. Par conséquent, ils apparaissent comme s'il s'agissait d'une table d'un serveur ou d'une instance.
Comment NE PAS utiliser les vues SQL Server ?
- Réutiliser la vue dans une autre vue qui sera réutilisée dans une autre vue encore. Bref, des vues profondément imbriquées. La réutilisation du code présente quelques inconvénients dans ce cas.
- Économisez sur les frappes. Il concerne le premier, qui réduit la pression des doigts et semble accélérer le codage.
Une mauvaise utilisation des vues, si elle est autorisée, masquera la véritable raison pour laquelle vous créez des vues. Comme vous le verrez plus tard, les avantages réels l'emportent sur les avantages perçus d'une utilisation inappropriée.
Exemple
Examinons un exemple de Microsoft. Le vEmployé vue depuis AdventureWorks . Voici le code :
-- Employee names and basic contact information
CREATE VIEW [HumanResources].[vEmployee]
AS
SELECT
e.[BusinessEntityID]
,p.[Title]
,p.[FirstName]
,p.[MiddleName]
,p.[LastName]
,p.[Suffix]
,e.[JobTitle]
,pp.[PhoneNumber]
,pnt.[Name] AS [PhoneNumberType]
,ea.[EmailAddress]
,p.[EmailPromotion]
,a.[AddressLine1]
,a.[AddressLine2]
,a.[City]
,sp.[Name] AS [StateProvinceName]
,a.[PostalCode]
,cr.[Name] AS [CountryRegionName]
,p.[AdditionalContactInfo]
FROM [HumanResources].[Employee] e
INNER JOIN [Person].[Person] p
ON p.[BusinessEntityID] = e.[BusinessEntityID]
INNER JOIN [Person].[BusinessEntityAddress] bea
ON bea.[BusinessEntityID] = e.[BusinessEntityID]
INNER JOIN [Person].[Address] a
ON a.[AddressID] = bea.[AddressID]
INNER JOIN [Person].[StateProvince] sp
ON sp.[StateProvinceID] = a.[StateProvinceID]
INNER JOIN [Person].[CountryRegion] cr
ON cr.[CountryRegionCode] = sp.[CountryRegionCode]
LEFT OUTER JOIN [Person].[PersonPhone] pp
ON pp.BusinessEntityID = p.[BusinessEntityID]
LEFT OUTER JOIN [Person].[PhoneNumberType] pnt
ON pp.[PhoneNumberTypeID] = pnt.[PhoneNumberTypeID]
LEFT OUTER JOIN [Person].[EmailAddress] ea
ON p.[BusinessEntityID] = ea.[BusinessEntityID];
GO
Le but de cette vue se concentre sur les informations de base des employés. Si nécessaire par un personnel des ressources humaines, il peut être affiché sur une page Web. A-t-il été réutilisé dans d'autres vues ?
Essayez ceci :
- Dans SQL Server Management Studio , recherchez AdventureWorks base de données.
- Développez le dossier Vues et recherchez [HumanResources].[vEmployee].
- Cliquez dessus avec le bouton droit de la souris et sélectionnez Afficher les dépendances .
Si vous voyez une autre vue en fonction de cette vue, qui dépend alors d'une vue différente, Microsoft nous a donné un mauvais exemple. Mais alors, il n'y a pas d'autres dépendances de vue.
Passons au suivant.
2. Démystifiez le mythe sur les vues SQL
Lorsque SQL Server traite un SELECT à partir d'une vue , il évalue le code dans la vue AVANT de traiter la clause WHERE ou toute jointure dans la requête externe. Avec plus de tables jointes, ce sera lent par rapport à un SELECT à partir des tables de base avec les mêmes résultats.
Du moins, c'est ce qu'on m'a dit quand j'ai commencé à utiliser SQL. Que ce soit un mythe ou non, il n'y a qu'une seule façon de le savoir. Passons à un exemple pratique.
Fonctionnement des vues SQL
Microsoft ne nous a pas laissé dans le noir pour débattre sans fin. Nous avons les outils pour voir comment les requêtes fonctionnent, comme STATISTICS IO et le plan d'exécution réel . Nous les utiliserons dans tous nos exemples. Prenons le premier.
USE AdventureWorks
GO
SELECT * FROM HumanResources.vEmployee e
WHERE e.BusinessEntityID = 105
Pour voir ce qui se passe lorsque SQL Server traite la vue, examinons le plan d'exécution réel dans la figure 1. Nous le comparons avec le code CREATE VIEW pour vEmployee dans la section précédente.
Comme vous pouvez le voir, les premiers nœuds traités par SQL Server sont ceux utilisant INNER JOIN. Ensuite, il procède au traitement des LEFT OUTER JOINs.
Étant donné que nous ne pouvons voir un nœud Filtre nulle part pour la clause WHERE, il doit se trouver dans l'un de ces nœuds. Si vous inspectez les propriétés de tous les nœuds, vous verrez la clause WHERE traitée dans la table Employee. Je l'ai enfermé dans une boîte dans la figure 1. Pour prouver qu'il est là, voir la figure 2 pour les propriétés de ce nœud :
Analyse
Donc, avait l'instruction SELECT dans le vEmployee vue a été évaluée ou traitée AVANT l'application de la clause WHERE ? Le plan d'exécution montre que non. Si c'était le cas, il devrait apparaître le plus près du nœud SELECT.
Ce qu'on m'a dit était un mythe. J'évitais quelque chose de bien à cause d'un malentendu sur l'utilisation correcte des vues SQL.
Maintenant que nous savons comment SQL Server traite un SELECT à partir d'une vue , la question demeure :est-ce plus lent que de ne pas utiliser de vue ?
SELECT FROM View ou SELECT FROM Tables de base – Laquelle s'exécutera le plus rapidement ?
Tout d'abord, nous devons extraire l'instruction SELECT dans le vEmployee view et produire le même résultat que nous avions lors de l'utilisation de la vue. Le code ci-dessous montre la même clause WHERE :
USE AdventureWorks
GO
-- SELECT FROM a view
SELECT * FROM HumanResources.vEmployee e
WHERE e.BusinessEntityID = 105
-- SELECT FROM Base Tables
SELECT
e.[BusinessEntityID]
,p.[Title]
,p.[FirstName]
,p.[MiddleName]
,p.[LastName]
,p.[Suffix]
,e.[JobTitle]
,pp.[PhoneNumber]
,pnt.[Name] AS [PhoneNumberType]
,ea.[EmailAddress]
,p.[EmailPromotion]
,a.[AddressLine1]
,a.[AddressLine2]
,a.[City]
,sp.[Name] AS [StateProvinceName]
,a.[PostalCode]
,cr.[Name] AS [CountryRegionName]
,p.[AdditionalContactInfo]
FROM [HumanResources].[Employee] e
INNER JOIN [Person].[Person] p
ON p.[BusinessEntityID] = e.[BusinessEntityID]
INNER JOIN [Person].[BusinessEntityAddress] bea
ON bea.[BusinessEntityID] = e.[BusinessEntityID]
INNER JOIN [Person].[Address] a
ON a.[AddressID] = bea.[AddressID]
INNER JOIN [Person].[StateProvince] sp
ON sp.[StateProvinceID] = a.[StateProvinceID]
INNER JOIN [Person].[CountryRegion] cr
ON cr.[CountryRegionCode] = sp.[CountryRegionCode]
LEFT OUTER JOIN [Person].[PersonPhone] pp
ON pp.BusinessEntityID = p.[BusinessEntityID]
LEFT OUTER JOIN [Person].[PhoneNumberType] pnt
ON pp.[PhoneNumberTypeID] = pnt.[PhoneNumberTypeID]
LEFT OUTER JOIN [Person].[EmailAddress] ea
ON p.[BusinessEntityID] = ea.[BusinessEntityID]
WHERE e.BusinessEntityID = 105
Ensuite, nous inspectons l'OI STATISTIQUES et effectuons un Comparer Showplan . De combien de ressources une requête à partir d'une vue aura-t-elle besoin par rapport à une requête à partir de tables de base ? Voir Figure 3.
Ici, interroger à partir d'une vue ou de tables de base consommera les mêmes lectures logiques. Les deux utilisaient des pages de 19 * 8 Ko. Sur cette base, c'est une égalité sur qui est le plus rapide. En d'autres termes, l'utilisation d'une vue n'affectera pas les performances. Comparons le plan d'exécution réel des deux en utilisant le Comparer Showplan :
Voyez-vous la partie ombrée du diagramme ? Qu'en est-il du QueryPlanHash des deux? Étant donné que les deux requêtes ont le même QueryPlanHash et les mêmes opérations, qu'il s'agisse de tables de vue ou de base, seront traitées de la même manière par SQL Server .
Les mêmes lectures logiques et le même plan de l'échantillon nous indiquent que les deux effectueront la même chose. Ainsi, avoir des lectures logiques élevées ralentira l'exécution de votre requête, que vous utilisiez des vues ou non. Connaître ce fait vous aidera à résoudre le problème et à accélérer l'exécution de votre vue.
Malheureusement, il y a de mauvaises nouvelles.
Joindre des vues SQL à des tables
Ce que vous avez vu précédemment est un SELECT d'une vue sans jointure. Cependant, que se passe-t-il si vous joignez une table à une vue ?
Reprenons un autre exemple. Cette fois, nous utilisons le vSalesPerson afficher dans AdventureWorks – une liste de vendeurs avec coordonnées et quota de vente. Encore une fois, nous comparons l'instruction à un SELECT des tables de base :
-- get the total sales orders for each salesperson
-- using the view joined with SalesOrderHeader
SELECT
sp.FirstName
,sp.MiddleName
,sp.LastName
,SUM(soh.TotalDue) AS TotalSalesOrders
FROM Sales.vSalesPerson sp
INNER JOIN Sales.SalesOrderHeader soh ON sp.BusinessEntityID = soh.SalesPersonID
GROUP BY sp.LastName, sp.MiddleName, sp.FirstName
-- using base tables
SELECT
p.FirstName
,p.MiddleName
,p.LastName
,SUM(soh.TotalDue) AS TotalSalesOrders
FROM sales.SalesPerson sp
INNER JOIN Person.Person p ON sp.BusinessEntityID = P.BusinessEntityID
INNER JOIN Sales.SalesOrderHeader soh ON sp.BusinessEntityID = soh.SalesPersonID
GROUP BY p.LastName, p.MiddleName, p.FirstName
Si vous pensez que ce sera également la même chose, vérifiez l'E/S STATISTIQUES :
Surpris? Rejoindre le vSalesPerson vue avec le SalesOrderHeader table nécessite des ressources énormes (28 240 x 8 Ko) par rapport à la simple utilisation des tables de base (774 x 8 Ko). Notez également qu'il comprenait des tableaux dont nous n'avions pas besoin (les tableaux dans les cases rouges). Sans parler des lectures logiques supérieures sur SalesOrderHeader lors de l'utilisation de la vue.
Mais ça ne s'arrête pas là.
Le plan d'exécution réel en dit plus
Notez le plan d'exécution réel de la requête aux tables de base :
L'illustration semble montrer un plan d'exécution assez normal. Mais jetez un coup d'œil à celui avec la vue :
Le plan d'exécution de la figure 7 coïncide avec l'ES STATISTIQUES de la figure 5. Nous pouvons voir les tables dont nous n'avons pas besoin dans la vue. Il existe également une recherche de clé nœud avec une estimation de ligne qui dépasse de plus d'un millier d'enregistrements les lignes réelles. Enfin, un avertissement dans le nœud SELECT apparaît également. Qu'est-ce que cela pourrait être ?
Qu'est-ce que cette subvention excessive ? avertissement dans le noeud SELECT ?
Une attribution excessive se produit lorsque la mémoire maximale utilisée est trop petite par rapport à la mémoire accordée. Dans ce cas, 1 024 Ko ont été accordés, mais seuls 16 Ko ont été utilisés.
L'allocation de mémoire correspond à la quantité estimée de mémoire en Ko requise pour exécuter le plan.
Il peut s'agir de mauvaises estimations dans la Key Lookup node et/ou l'inclusion des tables dont nous n'avions pas besoin dans le plan qui a causé cela. De plus, trop de mémoire allouée peut provoquer un blocage. Les 1008 Ko restants auraient pu être utiles pour d'autres opérations.
Finalement, quelque chose s'est mal passé lorsque vous avez rejoint la vue avec une table. Nous n'avons pas à gérer ces problèmes si nous interrogeons à partir des tables de base.
À emporter
C'était une longue explication. Cependant, nous savons que les vues ne sont pas évaluées ou traitées AVANT qu'une clause WHERE ou des jointures dans la requête externe ne soient évaluées. Nous avons également prouvé que les deux auraient les mêmes performances.
D'autre part, il y a un cas où nous joignons une vue à une table. Il utilise des jointures de tables dont nous n'avons pas besoin dans la vue. Ils nous sont invisibles sauf si nous vérifions les STATISTICS IO et le plan d'exécution réel. Tout cela peut nuire aux performances et les problèmes peuvent venir de nulle part.
Par conséquent :
- Nous devons savoir comment les requêtes, y compris les vues, fonctionnent de l'intérieur.
- STATISTICS IO et Actual Execution Plans révéleront le fonctionnement des requêtes et des vues.
- Nous ne pouvons pas simplement joindre une vue à une table et la réutiliser sans précaution. Vérifiez toujours les E/S STATISTIQUES et les plans d'exécution réels ! Au lieu de réutiliser les vues et de les imbriquer pour une productivité de codage "améliorée", j'utilise un IntelliSense et l'outil de complétion de code comme SQL Complete.
Nous pouvons alors être sûrs de ne pas écrire des vues qui auront des résultats corrects mais fonctionneront comme un escargot.
3. Essayez les vues indexées
Les vues indexées sont ce que le nom implique. Cela peut améliorer les performances des instructions SELECT. Mais comme les index de table, cela peut avoir un impact sur les performances si les tables de base sont volumineuses et mises à jour en permanence.
Pour voir comment les vues indexées peuvent améliorer les performances des requêtes, examinons la vStateProvinceCountryRegion afficher dans AdventureWorks . La vue est indexée sur StateProvinceID et CountryRegionCode . Il s'agit d'un index clusterisé et unique.
Comparons les STATISTICS IO de la vue n'ayant pas l'index et ayant un index. Avec cela, nous apprenons combien de pages de 8 Ko notre serveur SQL lira :
La figure montre qu'avoir un index dans la vStateProvinceCountryRegion view réduit de moitié les lectures logiques. C'est une amélioration de 50 % par rapport à l'absence d'index.
C'est bon à entendre.
Encore une fois, n'ajoutez pas d'index à vos vues par inadvertance. En plus d'avoir une longue liste de règles strictes pour avoir 1 index clusterisé unique, cela peut nuire aux performances, un peu comme l'ajout d'index aux tables avec désinvolture. Vérifiez également les STATISTICS IO s'il y a une diminution des lectures logiques après l'ajout de l'index.
À emporter
Comme nous l'avons vu dans notre exemple, les vues indexées peuvent améliorer les performances des vues SQL.
Astuce EN PRIME
Comme toute autre requête, les vues SQL s'exécuteront rapidement si :
- Les statistiques sont mises à jour
- Les index manquants sont ajoutés
- Les index sont défragmentés
- Les index ont utilisé le bon FILLFACTOR
Conclusion
Les vues SQL sont-elles bonnes ou mauvaises ?
Les vues SQL sont bonnes si nous les écrivons correctement et vérifions comment elles seront traitées. Nous avons des outils comme STATISTICS IO et Actual Execution Plan - utilisez-les ! Les vues indexées peuvent également améliorer les performances.
Comme ce poste? S'il vous plaît, partagez un peu d'amour sur votre plateforme de médias sociaux préférée.