Je travaille sur un système de reporting qui permet à l'utilisateur d'interroger arbitrairement un ensemble de tables de faits, contraignant sur plusieurs tables de dimension pour chaque table de faits. J'ai écrit une classe de générateur de requête qui assemble automatiquement toutes les jointures et sous-requêtes correctes en fonction des paramètres de contrainte, et tout fonctionne comme prévu. Mais j'ai l'impression de ne pas générer les requêtes les plus efficaces. Sur un ensemble de tables contenant quelques millions d'enregistrements, ces requêtes prennent environ 10 secondes à s'exécuter, et j'aimerais les réduire à moins d'une seconde. J'ai le sentiment que, si je pouvais me débarrasser des sous-requêtes, le résultat serait beaucoup plus efficace. Plutôt que de vous montrer mon schéma actuel (ce qui est beaucoup plus compliqué), je vais vous montrer un exemple analogue qui illustre le point sans avoir à expliquer l'ensemble de mon application et de mon modèle de données. Imaginez que j'ai une base de données d'informations sur les concerts, avec des artistes et des lieux. Les utilisateurs peuvent arbitrairement étiqueter les artistes et les sites. Donc, le schéma ressemble à ceci:Requêtes de création de rapports: meilleur moyen de rejoindre plusieurs tableaux de faits?
concert
id
artist_id
venue_id
date
artist
id
name
venue
id
name
tag
id
name
artist_tag
artist_id
tag_id
venue_tag
venue_id
tag_id
Assez simple. Maintenant, disons que je veux interroger la base de données pour tous les concerts qui se déroulent dans un mois d'aujourd'hui, pour tous les artistes avec des tags 'techno' et 'trombone', en concert avec 'cheap-beer' et 'great-mosh' étiquette de -pits.
La meilleure question que je suis en mesure de trouver ressemble à ceci:
SELECT
concert.id AS concert_id,
concert.date AS concert_date,
artist.id AS artist_id,
artist.name AS artist_name,
venue.id AS venue_id,
venue.name AS venue_name,
FROM
concert
INNER JOIN (
artist ON artist.id = concert.artist_id
) INNER JOIN (
venue ON venue.id = concert.venue_id
)
WHERE (
artist.id IN (
SELECT artist_id
FROM artist_tag
INNER JOIN tag AS a on (
a.id = artist_tag.tag_id
AND
a.name = 'techno'
) INNER JOIN tag AS b on (
b.id = artist_tag.tag_id
AND
b.name = 'trombone'
)
)
AND
venue.id IN (
SELECT venue_id
FROM venue_tag
INNER JOIN tag AS a on (
a.id = venue_tag.tag_id
AND
a.name = 'cheap-beer'
) INNER JOIN tag AS b on (
b.id = venue_tag.tag_id
AND
b.name = 'great-mosh-pits'
)
)
AND
concert.date BETWEEN NOW() AND (NOW() + INTERVAL 1 MONTH)
)
La requête fonctionne, mais je vraiment n'aime pas avoir ces multiples sous-requêtes. Si je pouvais accomplir la même logique en utilisant simplement la logique JOIN, j'ai le sentiment que la performance s'améliorerait considérablement.
Dans un monde parfait, j'utiliserais un vrai serveur OLAP. Mais mes clients vont se déployer sur MySQL ou MSSQL ou Postgres, et je ne peux pas garantir qu'un moteur compatible OLAP sera disponible. Je suis donc bloqué en utilisant un SGBDR ordinaire avec un schéma en étoile. Ne vous méprenez pas trop sur les détails de cet exemple (ma vraie application n'a rien à voir avec la musique, mais elle a plusieurs tables de faits avec une relation analogue à celles que j'ai montrées ici). Dans ce modèle, les tables 'artist_tag' et 'lieu_tag' fonctionnent comme des tables de faits, et tout le reste est une dimension.
Il est important de noter, dans cet exemple, que les requêtes sont beaucoup plus simples à écrire si j'autorise uniquement l'utilisateur à contraindre par rapport à une seule valeur artist_tag ou lieu_tag. Cela devient vraiment compliqué quand j'autorise les requêtes à inclure la logique ET, nécessitant plusieurs balises distinctes. Donc, ma question est la suivante: quelles sont les meilleures techniques que vous connaissez pour écrire des requêtes efficaces contre plusieurs tables de faits?
je pense que le nœud de la question est vraiment ici la nature AND de la requête, plutôt que les "tables de faits multiples". (Bien qu'ils se complètent mutuellement.) La réponse que je donne ci-dessous résout cela en exécutant le composant AND de la requête dans une clause HAVING, au lieu de nécessiter plusieurs fois les jointures aux mêmes tables de faits. – MatBailie
Il est temps de marquer comme résolu/fermé/... :) –