Optimisation des requêtes SQL : L'impact de l'opérateur OR et les alternatives

Lors du développement d'applications ou de l'optimisation de bases de données, il est fréquent de rencontrer des problèmes de performance avec des requêtes SQL. Un cas typique survient lorsque des requêtes qui semblent logiquement simples prennent un temps d'exécution excessivement long. L'une des causes les plus courantes de cette dégradation de performance est l'utilisation inappropriée de l'opérateur OR dans la clause WHERE.

Considérons un scénario où deux tables, Utilisateurs et Utilisateurs_Temporaire, possèdent une structure identique. Chaque table contient des informations sur des utilisateurs, avec des champs comme un identifiant, un nom et un numéro de contact. Nous devons identifier les enregistrements pour lesquels le nom ou le numéro de contact sont identiques entre les deux tables, à l'exception des identifiants (qui doivent être différents).

Voici la structure simplifiée d'une table Utilisateurs :

CREATE TABLE Utilisateurs
(
    IDUtilisateur INT PRIMARY KEY,
    NomUtilisateur VARCHAR(50),
    ContactTelephone VARCHAR(50)
);
GO

La table Utilisateurs_Temporaire a la même structure. Supposons que Utilisateurs contienne environ 7000 entrées uniques et Utilisateurs_Temporaire environ 2000.

Une première approche, intuitive mais souvent inefficace, pourrait être de rédiger la requête comme suit :

SELECT U.IDUtilisateur, U.NomUtilisateur, U.ContactTelephone
FROM Utilisateurs U
JOIN Utilisateurs_Temporaire UT ON (U.NomUtilisateur = UT.NomUtilisateur OR U.ContactTelephone = UT.ContactTelephone)
WHERE U.IDUtilisateur <> UT.IDUtilisateur;

Bien que cette instruction soit logiquement correcte et concise, elle peut entraîner un temps d'exécution considérable, transformant une opération qui devrait être rapide en une tâche de plusieurs secondes.

Pourquoi l'opérateur OR est-il problématique ?

L'opérateur OR, en particulier lorsqu'il est appliqué à des colonnes différentes ou à des colonnes où l'une n'est pas indexée, peut empêcher le moteur de base de données d'utiliser efficacement les index disponibles. Le plan d'exécution peut alors opter pour une analyse complète de la table (full table scan), ce qui est extrêmement coûteux en ressources et en temps pour de grands volumes de données.

Alternative performante : Utilisation de UNION

Pour contourner cette limitation, une stratégie consiste à scinder la requête en plusieurs requêtes plus simples, chacune gérant une partie de la condition OR, puis à combiner leurs résultats à l'aide de l'opérateur UNION. Cette approche permet au moteur de base de données d'utiliser les index de manière plus efficace pour chaque sous-requête.

-- Recherche des utilisateurs avec le même nom mais des identifiants distincts
SELECT U.IDUtilisateur, U.NomUtilisateur, U.ContactTelephone
FROM Utilisateurs U
INNER JOIN Utilisateurs_Temporaire UT ON U.NomUtilisateur = UT.NomUtilisateur AND U.IDUtilisateur <> UT.IDUtilisateur

UNION

-- Ajout des utilisateurs avec le même numéro de contact mais des identifiants distincts
SELECT U.IDUtilisateur, U.NomUtilisateur, U.ContactTelephone
FROM Utilisateurs U
INNER JOIN Utilisateurs_Temporaire UT ON U.ContactTelephone = UT.ContactTelephone AND U.IDUtilisateur <> UT.IDUtilisateur;

L'exécution de cette version de la requête est souvent quasi-instantanée. Chaque sous-requête peut désormais exploiter pleinement les index créés sur NomUtilisateur et ContactTelephone, car les conditions sont simples et directes.

UNION vs. UNION ALL

Il est important de noter la différence entre UNION et UNION ALL :

  • UNION combine les résultats de plusieurs requêtes et supprime les lignes dupliquées. Cette opération de suppression des doublons nécessite un tri interne, ce qui peut ajouter un surcoût.
  • UNION ALL combine simplement les résultats de plusieurs requêtes sans vérifier ni supprimer les doublons. Il est généralement plus rapide que UNION lorsque la suppression des doublons n'est pas nécessaire ou gérée autrement.

Dans l'exemple ci-dessus, UNION est utilisé car une personne peut avoir à la fois le même nom ET le même numéro de téléphone qu'une entrée temporaire, ce qui créerait des doublons si UNION ALL était utilisé.

Autres cas où l'opérateur OR est à éviter

La règle d'éviter OR s'étend à d'autres situations. Par exemple, pour filtrer des enregistrements basés sur plusieurs valeurs spécifiques pour une même colonne :

SELECT IDUtilisateur, NomUtilisateur, ContactTelephone
FROM Utilisateurs
WHERE NomUtilisateur = 'Alice' OR NomUtilisateur = 'Bob';

Bien que l'opérateur IN puisse sembler être une meilleure alternative :

SELECT IDUtilisateur, NomUtilisateur, ContactTelephone
FROM Utilisateurs
WHERE NomUtilisateur IN ('Alice', 'Bob');

Certains moteurs de bases de données peuvent toujours traiter la clause IN de manière similaire à une série de OR, entraînant potentiellement une analyse de table complète si le nombre de valeurs est trop élevé ou si l'optimiseur ne peut pas utiliser l'index.

Si la condition porte sur une plage de valeurs continues, l'opérateur BETWEEN...AND est l'option la plus performante :

SELECT IDUtilisateur, NomUtilisateur, ContactTelephone
FROM Utilisateurs
WHERE IDUtilisateur BETWEEN 100 AND 200;

Pour des valeurs discrètes où l'IN est douteux et OR est lent, la décomposition en UNION ALL est souvent la meilleure solution :

SELECT IDUtilisateur, NomUtilisateur, ContactTelephone FROM Utilisateurs WHERE NomUtilisateur = 'Charlie'
UNION ALL
SELECT IDUtilisateur, NomUtilisateur, ContactTelephone FROM Utilisateurs WHERE NomUtilisateur = 'David';

Dans ce cas, UNION ALL est privilégié car il est peu probable que les conditions "NomUtilisateur = 'Charlie'" et "NomUtilisateur = 'David'" génèrent des doublons, optimisant ainsi la performence en évitant le tri.

Étiquettes: SQL Optimisation_Requêtes Performances_Base_de_Données UNION_ALL Opérateur_OR

Publié le 30 juillet à 00h35