Tester si on a des enregistrements

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 :

ChargeElevee

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.

ChargeFaible

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.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Contrôle de sécurité * Time limit is exhausted. Please reload CAPTCHA.