La préparation d'un lot SQL séparément de l'exécution du lot SQL préparé est une construction qui est effectivement **inutile pour SQL Server étant donné la façon dont les plans d'exécution sont mis en cache. Séparer les étapes de préparation (analyse, liaison des paramètres et compilation) et d'exécution n'a de sens que lorsqu'il n'y a pas de mise en cache. Le but est de gagner du temps sur l'analyse et la compilation en réutilisant un plan existant, et c'est ce que fait le cache du plan interne. Mais tous les SGBDR ne font pas ce niveau de mise en cache (clairement à cause de l'existence de ces fonctions) et il est donc laissé au code client de demander qu'un plan soit "mis en cache", et donc de sauvegarder cet ID de cache, puis de réutiliser il. C'est une charge supplémentaire sur le code client (et le programmeur de se rappeler de le faire et de bien faire les choses) qui n'est pas nécessaire. En fait, la page MSDN de la méthode IDbCommand.Prepare () indique:
Le serveur met automatiquement en cache les plans de réutilisation si nécessaire; par conséquent, il n'est pas nécessaire d'appeler cette méthode directement dans votre application cliente.
Il convient de noter que, pour autant que mes tests le montrent (ce qui correspond à ce que vous montrez dans la question), appeler SqlCommand.Prepare () , ne fait pas une opération de "préparation" uniquement: il appelle sp_prepexec qui prépare et exécute le SQL ; il n'appelle pas sp_prepare qui est uniquement d'analyse et de compilation (et n'accepte aucune valeur de paramètre, uniquement leurs noms et types de données). Par conséquent, il ne peut y avoir aucun avantage de «précompilation» de l'appel SqlCommand.Prepare
car il effectue une exécution immédiate (en supposant que le but est de différer l'exécution). CEPENDANT , il y a un avantage mineur potentiel intéressant d'appeler SqlCommand.Prepare()
, même s'il appelle sp_prepexec
au lieu de sp_prepare
. Veuillez consulter la note (** ) en bas pour plus de détails.
Mes tests montrent que même lorsque l' SqlCommand.Prepare()
on appelle (et veuillez noter que cet appel ne pas émettre de commandes sur SQL Server), le ExecuteNonQuery
ou ExecuteReader
qui suit est entièrement exécutée, et si elle est ExecuteReader, il renvoie des lignes. Néanmoins, SQL Server Profiler montre que le premier SqlCommand.Execute______()
appel après l' SqlCommand.Prepare()
appel est enregistré en tant qu'événement «Préparer SQL» et que les .Execute___()
appels suivants s'enregistrent en tant qu'événements «Exec Prepared SQL».
Code de test
Il a été téléchargé sur PasteBin à: http://pastebin.com/Yc2Tfvup . Le code crée une application console .NET / C # qui est destinée à être exécutée pendant l'exécution d'une trace SQL Server Profiler (ou une session d'événements étendus). Il marque une pause après chaque étape afin de savoir clairement quelles instructions ont un effet particulier.
MISE À JOUR
Trouvé plus d'informations et une légère raison potentielle pour éviter d'appeler SqlCommand.Prepare()
. Une chose que j'ai remarquée lors de mes tests, et ce qu'il convient de noter pour quiconque exécute cette application console de test: il n'y a jamais d'appel explicite sp_unprepare
. J'ai fait quelques recherches et trouvé le message suivant sur les forums MSDN:
SqlCommand - Se préparer ou ne pas se préparer?
La réponse acceptée contient un tas d'informations, mais les points saillants sont:
- ** Ce que vous préparez réellement vous fait économiser, c'est principalement le temps nécessaire pour transmettre la chaîne de requête sur le fil.
- Une requête préparée n'a aucune garantie qu'un plan mis en cache est enregistré et n'est pas plus rapide qu'une requête ad-hoc une fois qu'elle atteint le serveur.
- Les RPC (CommandType.StoredProcedure) ne gagnent rien à se préparer.
- Sur le serveur, un descripteur préparé est essentiellement un index dans une carte en mémoire contenant TSQL à exécuter.
- La carte est stockée par connexion, aucune utilisation de connexion croisée n'est possible.
- La réinitialisation envoyée lorsque le client réutilise la connexion du pool efface la carte.
J'ai également trouvé le message suivant de l'équipe SQLCAT, qui semble être lié, mais pourrait simplement être un problème spécifique à ODBC alors que SqlClient se nettoie correctement lors de la suppression de SqlConnection. Il est difficile de dire que le post SQLCAT ne mentionne aucun test supplémentaire qui pourrait aider à prouver cela comme une cause, comme l'effacement du pool de connexions, etc.
Méfiez-vous de ces instructions SQL préparées
CONCLUSION
Étant donné que:
- le seul véritable avantage de l'appel
SqlCommand.Prepare()
semble être que vous n'avez pas besoin de soumettre à nouveau le texte de la requête sur le réseau,
- appeler
sp_prepare
et sp_prepexec
stocker le texte de la requête dans la mémoire de la connexion (c'est-à-dire la mémoire SQL Server)
Je recommande contre l' appel SqlCommand.Prepare()
parce que le seul avantage potentiel est de sauver les paquets réseau alors que la baisse prend plus de mémoire de serveur. Bien que la quantité de mémoire consommée soit probablement très faible dans la majorité des cas, la bande passante réseau est rarement un problème, car la plupart des serveurs de base de données sont directement connectés aux serveurs d'applications via des connexions minimales de 10 mégabits (plus probablement 100 mégabits ou gigabits de nos jours) ( et certains sont même sur la même boîte ;-). Cela et la mémoire sont presque toujours une ressource plus rare que la bande passante réseau.
Je suppose que pour quiconque écrit un logiciel pouvant se connecter à plusieurs bases de données, la commodité d'une interface standard pourrait changer cette analyse coûts / avantages. Mais même dans ce cas, je dois croire qu'il est encore assez facile d'abstraire une interface DB "standard" qui permet à différents fournisseurs (et donc des différences entre eux, comme le fait de ne pas avoir besoin d'appeler Prepare()
) étant donné que cela est un objet de base Programmation orientée que vous faites probablement déjà dans votre code d'application ;-).