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

Problèmes avec les performances des paramètres de valeur de table

Si les TVP sont "sensiblement plus lents" que les autres options, il est fort probable que vous ne les implémentiez pas correctement.

  1. Vous ne devez pas utiliser un DataTable, à moins que votre application ne l'utilise en dehors de l'envoi des valeurs au TVP. Utilisation de IEnumerable<SqlDataRecord> L'interface est plus rapide et utilise moins de mémoire car vous ne dupliquez pas la collection en mémoire uniquement pour l'envoyer à la base de données. J'ai ceci documenté dans les endroits suivants :
  2. Vous ne devez pas utiliser AddWithValue pour le SqlParameter, bien qu'il ne s'agisse probablement pas d'un problème de performances. Mais quand même, ça devrait être :

    SqlParameter tvp = com.Parameters.Add("data", SqlDbType.Structured);
    tvp.Value = MethodThatReturnsIEnumerable<SqlDataRecord>(MyCollection);
    
  3. Les TVP sont des variables de table et, en tant que telles, ne conservent pas de statistiques. Cela signifie qu'ils signalent n'avoir qu'une seule ligne à l'optimiseur de requête. Donc, dans votre proc, soit :
    • Utilisez la recompilation au niveau de l'instruction sur toutes les requêtes utilisant le TVP pour autre chose qu'un simple SELECT :OPTION (RECOMPILE)
    • Créer une table temporaire locale (c'est-à-dire un seul # ) et copiez le contenu du TVP dans la table temporaire
    • Vous pouvez essayer d'ajouter une clé primaire en cluster au type de table défini par l'utilisateur
    • Si vous utilisez SQL Server 2014 ou une version plus récente, vous pouvez essayer d'utiliser les tables OLTP en mémoire/optimisées en mémoire. Veuillez consulter :Table temporaire et variable de table plus rapides grâce à l'optimisation de la mémoire

Concernant pourquoi vous voyez :

insert into @data ( ... fields ... ) values ( ... values ... )
-- for each row
insert into @data ( ... fields ... ) values ( ... values ... )

au lieu de :

insert into @data ( ... fields ... ) 
values ( ... values ... ),
       ( ... values ... ),

SI c'est bien ce qui se passe, alors :

  • Si les insertions sont effectuées dans le cadre d'une transaction, il n'y a pas de réelle différence de performances
  • La nouvelle syntaxe de la liste de valeurs (c'est-à-dire VALUES (row1), (row2), (row3) ) est limité à quelque chose comme 1000 lignes et n'est donc pas une option viable pour les TVP qui n'ont pas cette limite. CEPENDANT, ce n'est probablement pas la raison pour laquelle des insertions individuelles sont utilisées, étant donné qu'il n'y a pas de limite lors de l'utilisation de INSERT INTO @data (fields) SELECT tab.[col] FROM (VALUES (), (), ...) tab([col]) , que j'ai documenté ici :Nombre maximal de lignes pour le constructeur de valeurs de table . Au lieu de cela...
  • La raison est probablement que les insertions individuelles permettent de diffuser les valeurs du code de l'application vers SQL Server :
    1. à l'aide d'un itérateur (c'est-à-dire le IEnumerable<SqlDataRecord> noté au n° 1 ci-dessus), le code de l'application envoie chaque ligne telle qu'elle est renvoyée par la méthode, et
    2. construire les VALUES (), (), ... list, même en faisant le INSERT INTO ... SELECT FROM (VALUES ...) approche (qui n'est pas limitée à 1000 lignes), qui nécessiterait toujours de construire l'entier VALUES liste avant d'envoyer tout des données dans SQL Server. S'il y a beaucoup de données, cela prendrait plus de temps pour construire la chaîne super longue, et cela prendrait beaucoup plus de mémoire en le faisant.

Veuillez également consulter ce livre blanc de l'équipe de conseil client SQL Server :Maximiser le débit avec TVP