Il m’est assez souvent arrivé de voir des développeurs qui, pour savoir si leur programme avait des informations à traiter, comptaient le nombre d’enregistrement vérifiant leurs critères et lançaient leur traitement si ce nombre était différent de zéro. Mais en pratique, et surtout du point de vue des performances, cette façon de développer est plutôt catastrophique.
Dans un premier temps, mettons en place notre jeu d’essai :
CREATE TABLE MaTable (Id int identity PRIMARY KEY, Valeur varchar(36)); GO INSERT INTO MaTable (Valeur) SELECT CONVERT(varchar(36),NEWID()) GO 1000000
Mettons ensuite en place la fameuse procédure de comptage :
CREATE PROCEDURE Comptage AS BEGIN declare @Nb int select @nb=count(*) from MaTable where Valeur like '1%' if @Nb>0 SELECT 'Enreg à traiter' else SELECT 'Aucun enreg' END GO
Regardons un peu ce que donne l’exécution de cette procédure en termes de charge IO et de CPU :
On voit dans l’exemple une charge de 6699 lectures logiques, ce qui est plutôt beaucoup pour simplement savoir si on a ou pas un enregistrement …
En fait, le problème vient du fait qu’on s’embête à comptabiliser le nombre exact d’enregistrements répondant à notre périmètre, à stocker ce nombre dans une variable, et ensuite à simplement tester si cette variable est non nulle. En enlevant une étape (le stockage dans une variable), on permet au système d’effectuer le minimum d’opérations, et de s’arrêter dès qu’un enregistrement a été trouvé.
CREATE PROCEDURE TestComptage AS BEGIN if (select count(*) from MaTable where Valeur like '1%')>0 SELECT 'Enreg à traiter' else SELECT 'Aucun enreg' END GO exec TestComptage
A l’exécution, cette fois-ci, on a juste 3 lectures (dans l’exemple), ce qui correspond à une charge IO divisée par 4000.
Et la charge UC est passée de 640ms à une valeur inférieure à la millliseconde.
On a donc désormais une procédure efficace. En fait, le système a été suffisamment intelligent pour comprendre le besoin, et transformer la requête comme si l’on avait utilisé la bonne syntaxe, avec le mot-clé EXISTS :
CREATE PROCEDURE Existence AS BEGIN if exists(select * from MaTable where Valeur like '1%') SELECT 'Enreg à traiter' else SELECT 'Aucun enreg' END GO
Les performances sont les mêmes que pour la requête précédente, et le plan d’exécution aussi est le même.
Le moteur est donc en mesure de rattraper quelques erreurs de développement, mais le fait de stocker le nombre d’enregistrements dans une variable faisait que, mathématiquement, le système n’avait pas le choix et devait faire le comptage complet pour, au final, rien.
Il est donc très important de réfléchir au besoin, afin de ne pas griller de la puissance inutilement.