Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Performances des variables de table dans SQL Server

Dans cet article, nous allons aborder le sujet des performances des variables de table. Dans SQL Server, nous pouvons créer des variables qui fonctionneront comme des tables complètes. Peut-être que d'autres bases de données ont les mêmes capacités, cependant, j'ai utilisé ces variables uniquement dans MS SQL Server.

Ainsi, vous pouvez écrire ce qui suit :

declare @t as table (int value)

Ici, nous déclarons la variable @t comme une table qui contiendra une seule colonne Value de type Integer. Il est possible de créer des tableaux plus complexes, cependant, dans notre exemple, une colonne suffit pour explorer l'optimisation.

Maintenant, nous pouvons utiliser cette variable dans nos requêtes. Nous pouvons y ajouter beaucoup de données et effectuer une récupération de données à partir de cette variable :

insert into @t
select UserID
from User
or
select * from @t

J'ai remarqué que les variables de table sont utilisées lorsqu'il est nécessaire de récupérer des données pour une grande sélection. Par exemple, il existe une requête dans le code qui renvoie les utilisateurs du site. Maintenant, vous collectez les identifiants de tous les utilisateurs, les ajoutez à la variable de table et pouvez rechercher des adresses pour ces utilisateurs. Peut-être quelqu'un peut-il demander pourquoi nous n'exécutons pas une requête sur la base de données et obtenons tout immédiatement ? J'ai un exemple simple.

Supposons que les utilisateurs proviennent du service Web, alors que leurs adresses sont stockées dans votre base de données. Dans ce cas, il n'y a pas d'issue. Nous avons obtenu un tas d'identifiants d'utilisateurs du service, et pour éviter d'interroger la base de données, quelqu'un décide qu'il est plus facile d'ajouter tous les identifiants au paramètre de requête en tant que variable de table et la requête aura une apparence soignée :

select *
from @t as users 
   join Address a on a.UserID = users.UserID
os

Tout cela fonctionne correctement. Dans le code C#, vous pouvez rapidement combiner les résultats des deux tableaux de données en un seul objet à l'aide de LINQ. Cependant, les performances de la requête peuvent en souffrir.

Le fait est que les variables de table n'ont pas été conçues pour traiter de gros volumes de données. Si je ne me trompe pas, l'optimiseur de requête utilisera toujours la méthode d'exécution LOOP. Ainsi, pour chaque ID de @t, une recherche dans la table d'adresses aura lieu. S'il y a 1000 enregistrements dans @t, le serveur analysera l'adresse 1000 fois.

En termes d'exécution, en raison du nombre insensé d'analyses, le serveur s'arrête simplement d'essayer de trouver des données.

Il est beaucoup plus efficace de parcourir toute la table d'adresses et de trouver tous les utilisateurs à la fois. Cette méthode s'appelle MERGE. Cependant, SQL Server le choisit lorsqu'il y a beaucoup de données triées. Dans ce cas, l'optimiseur ne sait pas combien et quelles données seront ajoutées à la variable, et s'il y a tri car une telle variable ne comprend pas d'index.

S'il y a peu de données dans la variable de table et que vous n'y insérez pas des milliers de lignes, tout va bien. Cependant, si vous aimez utiliser de telles variables et y ajouter une énorme quantité de données, vous devez continuer à lire.

Même si vous remplacez la variable de table par SQL, cela accélérera considérablement les performances des requêtes :

select *
from (
 Select 10377 as UserID
 Union all
 Select 73736
 Union all
 Select 7474748
 ….
  ) as users 
   join Address a on a.UserID = users.UserID

Il peut y avoir un millier d'instructions SELECT de ce type et le texte de la requête sera énorme, mais il sera exécuté des milliers de fois plus rapidement pour une grande masse de données car SQL Server peut choisir un plan d'exécution efficace.

Cette requête n'a pas l'air géniale. Cependant, son plan d'exécution ne peut pas être mis en cache car la modification d'un seul ID modifiera également l'ensemble du texte de la requête et les paramètres ne peuvent pas être utilisés.

Je pense que Microsoft ne s'attendait pas à ce que les utilisateurs utilisent des variables tabulaires de cette manière, mais il existe une solution de contournement intéressante.

Il existe plusieurs façons de résoudre ce problème. Cependant, à mon avis, le plus efficace en termes de performances est d'ajouter OPTION (RECOMPILE) à la fin de la requête :

select *
from @t as users 
   join Address a on a.UserID = users.UserID
OPTION (RECOMPILE)

Cette option est ajoutée une fois à la toute fin de la requête après même ORDER BY. Le but de cette option est de faire en sorte que SQL Server recompile la requête à chaque exécution.

Si nous mesurons les performances de la requête après cela, le temps sera probablement réduit pour effectuer la recherche. Avec des données volumineuses, l'amélioration des performances peut être significative, de quelques dizaines de minutes à quelques secondes. Désormais, le serveur compile son code avant d'exécuter chaque requête et n'utilise pas le plan d'exécution du cache, mais en génère un nouveau, en fonction de la quantité de données dans la variable, et cela aide généralement beaucoup.

L'inconvénient est que le plan d'exécution n'est pas stocké et que le serveur doit compiler la requête et rechercher à chaque fois un plan d'exécution efficace. Cependant, je n'ai pas vu les requêtes où ce processus prenait plus de 100 ms.

Est-ce une mauvaise idée d'utiliser des variables de table ? Non, ce n'est pas le cas. N'oubliez pas qu'ils n'ont pas été créés pour les données volumineuses. Parfois, il est préférable de créer une table temporaire, s'il y a beaucoup de données, et d'insérer des données dans cette table, voire de créer un index à la volée. J'ai dû le faire avec des rapports, mais une seule fois. À l'époque, j'ai réduit le temps de génération d'un rapport de 3 heures à 20 minutes.

Je préfère utiliser une grande requête au lieu de la diviser en plusieurs requêtes et de stocker les résultats dans des variables. Autorisez SQL Server à régler les performances d'une grande requête et il ne vous laissera pas tomber. Veuillez noter que vous ne devez recourir aux variables de table que dans des cas extrêmes lorsque vous voyez vraiment leurs avantages.