Avant de passer à l'optimisation prématurée mode, il peut être utile de se pencher sur le modèle de requête suivant. Au moins, cela pourrait être utilisé comme référence par rapport à laquelle l'efficacité des optimisations possibles peut être mesurée.
SELECT T.Tagid, TagInfo.TagName, COUNT(*)
FROM Items I
JOIN Tags TagInfo ON TagInfo.TagId = T.TagId
JOIN ItemTagMap T ON I.ItemId = T.ItemId
--JOIN ItemTagMap T1 ON I.ItemId = T1.ItemId
WHERE I.ItemId IN
(
SELECT ItemId
FROM Items
WHERE -- Some typical initial search criteria
Title LIKE 'Bug Report%' -- Or some fulltext filter instead...
AND ItemDate > '02/22/2008'
AND Status = 'C'
)
--AND T1.TagId = 'MySql'
GROUP BY T.TagId, TagInfo.TagName
ORDER BY COUNT(*) DESC
La sous-requête est la "requête pilote", c'est-à-dire celle correspondant aux critères initiaux de l'utilisateur final. (voir ci-dessous pour plus de détails sur la façon dont cette requête, requise plusieurs fois, peut s'intégrer dans un flux global optimisé)Commenté est le JOIN sur T1 (et éventuellement T2, T3, lorsque plusieurs balises sont sélectionnées), et, avec la clause WHERE, le associé Critères. Ceux-ci sont nécessaires lorsque l'utilisateur sélectionne une balise particulière, que ce soit dans le cadre de la recherche initiale ou par raffinement. (Il peut être plus efficace de placer ces jointures et clauses where dans la sous-requête; plus d'informations à ce sujet ci-dessous)
Discussion... La "requête pilote", ou une variante de celle-ci, est nécessaire à deux fins distinctes :
-
1 pour fournir le complet liste d'ItemId qui est nécessaire pour énumérer toutes les balises associées.
-
2 pour fournir les N premières valeurs ItemId (N étant la taille de la page d'affichage), dans le but de rechercher des informations détaillées sur l'article dans le tableau des articles.
Notez que la liste complète n'a pas besoin d'être triée (ou qu'elle peut bénéficier d'un tri dans un ordre différent), la seconde liste devant être triée en fonction du choix de l'utilisateur (par exemple, par date, décroissant ou par titre, par ordre alphabétique croissant ). Notez également que si un ordre de tri est requis, le coût de la requête impliquera de traiter la liste complète (à moins d'une optimisation étrange par SQL lui-même et/ou d'une certaine dénormalisation, SQL doit "voir" les derniers enregistrements de cette liste , dans le cas où ils appartiennent au sommet, par tri).
Ce dernier fait, est en faveur d'avoir la même requête pour les deux fins, la liste correspondante peut être stockée dans une table temporaire. Le flux général serait de rechercher rapidement les N premiers enregistrements d'éléments avec leurs détails et de les renvoyer immédiatement à l'application. L'application peut alors obtenir ajax-fashion la liste des Tags pour les raffinements. Cette liste serait produite avec une requête semblable à celle ci-dessus, où la sous-requête est remplacée par un "select * from TemporaryTable". Il y a de fortes chances que l'optimiseur SQL décide de trier cette liste (dans certains cas), laissons-le faire, plutôt que de la deviner et de la trier explicitement.
Un autre point à considérer est peut-être d'amener la ou les jointures sur la table ItemTagMap à l'intérieur de la "requête de conduite" plutôt que comme indiqué ci-dessus. Il est probablement préférable de le faire, à la fois pour les performances et parce que cela produira la bonne liste pour l'objectif #2 (affichage d'une page d'éléments).
La requête/flux décrit ci-dessus évoluera probablement assez bien, même sur un matériel relativement modeste ; provisoirement dans le 1/2 million d'articles, avec des recherches d'utilisateurs soutenues pouvant aller jusqu'à 10 par seconde. L'un des facteurs clés serait la sélectivité des critères de recherche initiaux.
Idées d'optimisation
- [Selon les cas de recherche typiques et les statistiques de données], il peut être judicieux de dénormaliser en apportant (en fait en dupliquant) certains des champs des éléments à la table ItemTagMap. Les champs courts en particulier peuvent y être "bienvenus".
- Au fur et à mesure que les données augmentent dans le million d'éléments, nous pourrions exploiter la corrélation généralement forte de certaines balises (ex :dans SO, PHP est souvent fourni avec MySql, d'ailleurs souvent sans raison valable...), avec diverses astuces. Par exemple, l'introduction de TagIds "multi-Tag" pourrait rendre la logique d'entrée un peu plus compliquée, mais pourrait également réduire considérablement la taille de la carte.
-- 'pas dit ! --
L'architecture et les optimisations appropriées doivent être sélectionnées à la lumière des besoins réels et du profil statistique efficace des données...