Pourquoi certaines requêtes SQL sont-elles beaucoup plus lentes lorsqu'elles sont utilisées avec SqlCommand?
-
03-07-2019 - |
Question
J'ai une procédure stockée qui s'exécute beaucoup plus rapidement à partir de Sql Server Management Studio (2 secondes) que lorsqu'elle est exécutée avec System.Data.SqlClient.SqlCommand
(délai d'expiration après 2 minutes).
Quelle pourrait en être la raison?
Détails: Sql Server Management Studio s'exécute en 2 secondes (sur la base de production):
EXEC sp_Stat @DepartmentID = NULL
Dans .NET / C #, le délai suivant expire après 2 minutes (sur la base de données de production):
string selectCommand = @"
EXEC sp_Stat
@DepartmentID = NULL";
string connectionString = "server=***;database=***;user id=***;pwd=***";
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand(selectCommand, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
}
}
}
}
J'ai également essayé avec selectCommand = "sp_Stat",
, CommandType = StoredProcedure
et un SqlParameter
, mais le résultat est identique. / p>
Et sans EXEC
, le résultat est identique.
Dans une base de données de développement presque vide, les deux cas se terminent en moins d'une seconde. C'est donc lié au fait qu'il y a beaucoup de données dans la base de données, mais cela ne semble provenir que de .NET ...
Ce que Marc Gravell a écrit sur différentes valeurs SET
fait la différence dans le cas présenté.
SQL Server Profiler a montré que SQL Server Management Studio exécute les SET
suivants, contrairement au fournisseur de données client .NET Sql:
SET ROWCOUNT 0
SET TEXTSIZE 2147483647
SET NOCOUNT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ARITHABORT ON
SET LOCK_TIMEOUT -1
SET QUERY_GOVERNOR_COST_LIMIT 0
SET DEADLOCK_PRIORITY NORMAL
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SET ANSI_NULLS ON
SET ANSI_NULL_DFLT_ON ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
SET CURSOR_CLOSE_ON_COMMIT OFF
SET IMPLICIT_TRANSACTIONS OFF
SET QUOTED_IDENTIFIER ON
SET NOEXEC, PARSEONLY, FMTONLY OFF
Lorsque j'ai inclus ces éléments, la même requête a pris le même temps dans SSMS et .NET.
Et le SET
responsable est ...
SET ARITHABORT ON
Qu'est-ce que j'ai appris? Peut-être utiliser un profileur au lieu de deviner ...
(Au début, la solution semblait être liée à la détection de paramètres. Mais j'avais mélangé certaines choses ...)
La solution
Un autre élément important peut être le le SET
. options qui sont activées. Certaines de ces options modifient suffisamment le plan de requête pour modifier le profil. Certains peuvent avoir un impact énorme si vous regardez (par exemple) une colonne calculée + persistée (et éventuellement indexée): si les options SET
ne sont pas compatibles, il peut être obligé de recalculer les valeurs, plutôt que d'utiliser la valeur indexée - qui peut changer une recherche d'index en une analyse de table + calcul.
Essayez d’utiliser le profileur pour voir quelles options SET
sont "en cours de lecture", et voyez si l’utilisation de ces options change les choses.
La chaîne de connexion est un autre impact. par exemple, si vous activez MARS qui peut modifier le comportement de manière subtile.
Enfin, les transactions (implicites ( TransactionScope
) ou explicites) peuvent avoir un impact énorme , en fonction du niveau d'isolement.
Autres conseils
Cela est presque certainement dû à un plan de requête mis en cache «incorrect». C’est ce qui a été dit à quelques reprises sur SO.
Avez-vous des statistiques à jour? Un plan de maintenance d'index régulier?
Vous pouvez vérifier si cela est définitivement dû au plan de requête mis en cache en l'ajoutant à la définition de votre procédure stockée:
CREATE PROCEDURE usp_MyProcedure WITH RECOMPILE...
Ceci réindexera une base de données entière (attention si la base de données est très volumineuse!):
exec sp_msforeachtable "dbcc dbreindex('?')"
Messages SO:
Détection de paramètre (ou usurpation) dans SQL Server
Avait un problème similaire et il s'est avéré que MultipleActiveResultSets = true dans la chaîne de connexion (censée avoir un impact minimal) rendait l'extraction d'enregistrements de 1,5 million via une connexion distante prenant 25 minutes au lieu d'environ 2 minutes.
Nous avions un problème similaire: une requête se terminait en 2 secondes dans SSMS et prenait plus de 90 secondes lorsqu'elle était appelée depuis un client .NET (nous avons écrit plusieurs applications / sites VB / C # pour le tester.)
Nous avons suspecté que le plan de requête serait différent et nous l'avons réécrit avec des indications de boucle explicites ("jointure de boucle interne" et "avec index"). Cela a résolu le problème.