2009-02-18 3 views
2

J'ai une table appelée galerie. Pour chaque ligne galerie il y a plusieurs lignes dans le tableau image. Une photo appartient à une galerie. Ensuite, il y a le tableau vote. Chaque rangée est une upvote ou une downvote pour une certaine galerie. Voici la structure (simplifiée):Agrégation avec deux jointures (MySQL)

gallery (gallery_id) 
picture (picture_id, picture_gallery_ref) 
vote (vote_id, vote_value, vote_gallery_ref) 

Maintenant, je veux une requête pour me donner les informations suivantes: Toutes les galeries avec leurs propres champs de données et le nombre d'images qui sont connectés à la galerie et la valeur sumarized des votes.

Voici ma requête, mais en raison de la jointure multiple, les valeurs agrégées ne sont pas les bonnes. (Au moins quand il y a plus d'une ligne de soit des images ou des votes.)

SELECT 
    *, SUM(vote_value) as score, COUNT(picture_id) AS pictures 
FROM 
    gallery 
LEFT JOIN 
    vote 
    ON gallery_id = vote_gallery_ref 
LEFT JOIN 
    picture 
    ON gallery_id = picture_gallery_ref 
GROUP BY gallery_id 

Parce que je l'ai remarqué que COUNT(DISTINCT picture_id) me donne le bon nombre de photos que j'ai essayé ceci:

(SUM(vote_value)/GREATEST(COUNT(DISTINCT picture_id), 1)) AS score 

Il fonctionne dans cet exemple, mais que se passerait-il s'il y avait plus de jointures dans une requête? Je veux juste savoir s'il y a une meilleure ou plus «élégante» façon de résoudre ce problème. Aussi, j'aimerais savoir si ma solution est spécifique à MySQL ou SQL standard?

+0

votre GROUP BY utilise une colonne que vous n'avez pas définie, je suppose que vous vouliez dire gallery_id. – sfossen

+0

Oui, petite erreur – okoman

Répondre

5

Cette citation de William de Okham applique ici:

Enita non sunt multiplicanda praeter necessitatem

(latin pour "entités ne sont pas à multiplier au-delà de la nécessité").

Vous devriez reconsidérer pourquoi avez-vous besoin que cela soit fait dans une seule requête? Il est vrai qu'une requête unique a moins de frais que plusieurs requêtes, mais si la nature de cette seule requête devient trop complexe, que vous développiez et que le SGBDR exécute, puis exécutez des requêtes distinctes.

+0

Semble être le meilleur conseil puisqu'il n'y a évidemment pas de solution «élégante» dont je n'avais pas connaissance. – okoman

+0

Bah, maintenant je regrette de venir avec (et tester) ma requête ci-dessous; car la meilleure réponse à votre question est une non-réponse. –

+0

@matt b: Il est utile de montrer à quel point la solution doit être complexe, donc on peut décider avec plus de confiance qu'il serait plus simple d'exécuter des requêtes séparées. –

3

Ou tout simplement utiliser les sous-requêtes ...

Je ne sais pas si cela est valide la syntaxe MySQL, mais vous pourriez être en mesure de faire quelque chose de similaire à:

SELECT 
    gallery.*, a.score, b.pictures 
LEFT JOIN 
(
    select vote_gallery_ref, sum(vote_value) as score 
    from vote 
    group by vote_gallery_ref 
) a ON gallery_id = vote_gallery_ref 
LEFT JOIN 
(
    select picture_gallery_ref, count(picture_id) as pictures 
    from picture 
    group by picture_gallery_ref 
) b ON gallery_id = picture_gallery_ref 
0

Comme Bill Karwin dit , faire tout cela dans une seule requête est assez moche. Mais, si vous devez le faire, joindre et sélectionner des données non-agrégées avec des données agrégées nécessite de se joindre à des sous-requêtes (je n'ai pas beaucoup utilisé SQL ces dernières années, donc j'ai oublié le terme approprié pour cela).

Supposons que votre table de galerie comporte des champs supplémentaires name et state:

select g.gallery_id, g.name, g.state, i.num_pictures, j.sum_vote_values 
from gallery g 
inner join (
    select g.gallery_id, count(p.picture_id) as 'num_pictures' 
    from gallery g 
    left join picture p on g.gallery_id = p.picture_gallery_ref 
    group by g.gallery_id) as i on g.gallery_id = i.gallery_id 
left join (
    select g.gallery_id, sum(v.vote_value) as 'sum_vote_values' 
    from gallery g 
    left join vote v on g.gallery_id = v.vote_gallery_ref 
    group by g.gallery_id 
) as j on g.gallery_id = j.gallery_id 

Cela produira un jeu de résultats qui ressemble à:

gallery_id, name, state, num_pictures, sum_vote_values 
1, 'Gallery A', 'NJ', 4, 19 
2, 'Gallery B', 'NY', 3, 32 
3, 'Empty gallery', 'CT', 0, 
1

Combien de fois avez-vous ajouter/modifier les enregistrements vote?

À quelle fréquence ajoutez-vous/supprimez-vous des enregistrements d'images?

À quelle fréquence exécutez-vous cette requête pour ces totaux?

Il peut être préférable de créer des champs totaux sur la table de la galerie (total_pictures, total_votes, total_vote_values).

Lorsque vous ajoutez ou supprimez un enregistrement sur la table d'image, vous mettez également à jour le total sur la table de la galerie. Cela pourrait être fait en utilisant des déclencheurs sur la table d'image pour mettre à jour automatiquement la table de la galerie. Cela pourrait également être fait en utilisant une transaction combinant deux instructions SQL pour mettre à jour la table d'image et la table de la galerie. Lorsque vous ajoutez un enregistrement sur la table d'images, incrémentez le champ total_pictures sur la table de la galerie. Lorsque vous supprimez un enregistrement sur la table d'image, décrémentez le champ total_pictures.

Similaire lorsqu'un enregistrement de vote est ajouté ou supprimé ou que les modifications vote_value mettent à jour les champs total_votes et total_vote_values. L'ajout d'un enregistrement incrémente le champ total_votes et ajoute vote_values à total_vote_values. La suppression d'un enregistrement décrémente le champ total_votes et soustrait vote_values de total_vote_values. Mise à jour vote_values sur un enregistrement de vote devrait également mettre à jour total_vote_values avec la différence (soustraire l'ancienne valeur, ajouter une nouvelle valeur).

Votre requête devient maintenant triviale - c'est juste une requête simple de la table de la galerie. Mais cela se fait au détriment des mises à jour plus complexes de l'image et des tables de vote.

Questions connexes