L’opérateur `OR` en SQL est souvent source de problèmes de performance, comme le démontre une comparaison frappante : un même requête peut s’exécuter en 100 ms avec un `OR`, contre moins de 1 ms lorsqu’elle est reformulée sans cet opérateur. Ce phénomène s’explique par les mécanismes internes de planification des requêtes et l’utilisation des index, particulièrement dans des bases de données comme PostgreSQL.
Le cœur du problème réside dans la manière dont les systèmes de gestion de bases de données traitent les conditions `OR`. Contrairement à `AND`, qui permet de combiner efficacement des index (notamment via des index composites), `OR` oblige souvent le moteur à évaluer chaque condition séparément avant de fusionner les résultats, ce qui peut entraîner des lectures disques coûteuses ou des scans séquentiels de la table. Par exemple, une requête filtrant sur `submitter_id = :user_id OR reviewer_id = :user_id` devra soit scanner les deux index individuellement et fusionner les résultats (opération coûteuse en mémoire et CPU), soit effectuer un sequential scan si les valeurs sont trop fréquentes. Même avec des index sur les deux colonnes, cette approche reste inefficace, car elle ne bénéficie pas des optimisations conçues pour les jointures ou les filtres `AND`.
La solution passe souvent par une refonte de la structure des données ou de la requête. Une approche efficace consiste à remplacer le `OR` par une requête utilisant `UNION`, qui permet de tirer parti des index de manière plus directe. Par exemple, en décomposant la requête initiale en deux sous-requêtes (une pour `submitter_id`, une pour `reviewer_id`) puis en additionnant leurs résultats, on obtient une exécution près de 100 fois plus rapide. Une autre stratégie, plus architecturale, implique de normaliser la schéma en créant une table intermédiaire (comme `application_user`) qui centralise les relations entre utilisateurs et applications. Cette table, indexée sur `user_id`, permet des requêtes linéaires et prévisibles, évitant ainsi les pièges du `OR`.
Les optimiseurs de requêtes sont principalement conçus pour des opérations `AND` ou des recherches ponctuelles, car ces cas sont les plus courants (comme les filtres dans un moteur de recherche en ligne). Les conditions `OR` introduisent une complexité supplémentaire, car elles augmentent le volume de données à traiter (contrairement à `AND`, qui le réduit). Pour les schémas où plusieurs colonnes partagent des caractéristiques similaires (comme des clés étrangères pointant vers une même table), il est souvent préférable de regrouper ces données dans une structure unifiée. Par exemple, en utilisant l’héritage de tables ou une table parent commune (comme `writing` pour les posts et commentaires), on peut créer des index optimisés et simplifier les requêtes.
Enfin, bien que des améliorations comme les statistiques étendues ou les patchs récents (comme celui mentionné pour PostgreSQL 18) puissent atténuer certains problèmes, elles ne résolvent pas fondamentalement la difficulté inhérente à `OR`. La clé reste une conception attentive de la schéma et une reformulation proactive des requêtes pour privilégier les opérations `AND` et les jointures, plus alignées avec les forces des optimiseurs SQL modernes.