Une requête SQL difficile: la popularité des balises pour les modèles avec des associations complexes
Question
Je ne suis même pas sûr que ce soit possible, mais voici mon problème:
J'écris ce qui est essentiellement un moteur de blog dans lequel un article de blog et toutes les réponses à chaque article de blog peuvent être étiquetés.
Je pourrais donc avoir un article de blog étiqueté "pile" et une réponse à cet article étiqueté "débordement".
Pour le moment, j'essaie de générer une liste des balises les plus populaires lorsqu'un utilisateur clique sur une page spéciale de mon application. Il doit renvoyer non seulement les n balises les plus populaires classées par nombre décroissant de publications sur le blog, mais également le nombre de publications associées à chaque balise, même si une réponse dans cette publication mais pas dans la publication elle-même est marquée avec cette balise .
Ainsi, si BlogPost A est étiqueté avec "foo", et qu'une réponse dans BlogPost B est étiquetée avec "foo", le résumé de balise populaire doit compter cela comme deux articles de blog au total, même si BlogPost B n'est pas étiquetés techniquement.
Voici une description des tables / champs pouvant être pertinents:
BlogPosts
| id # Primary key for all tables, Rails-style
BlogComments
| id
| blog_post_id
Tags
| id
| name # 'foo'
Taggings
| id
| tag_id
| blog_post_id
| blog_comment_id
Il existe une certaine dénormalisation dans Taggings pour des raisons de commodité. Si quelqu'un taggue BlogPost, il renseigne le champ blog_post_id et blog_comment_id reste NULL. Si quelqu'un associe un commentaire à une publication, il remplit blog_post_id et blog_comment_id.
Existe-t-il un moyen de renvoyer une liste triée des balises les plus populaires dans une ou plusieurs requêtes SQL? Je pense que je devrais peut-être simplement exécuter un script coûteux toutes les quelques minutes sur un travail cron et afficher la sortie en cache au lieu de l'exécuter à chaque fois que quelqu'un clique sur la page ...
Merci!
La solution
Jusqu'à présent, je ne vois rien de compliqué dans votre demande:
SELECT
tag_id,
COUNT(blog_post_id) + COUNT(blog_comment_id) tag_count
FROM
Taggings
GROUP BY
tag_id
ORDER BY
COUNT(blog_post_id) + COUNT(blog_comment_id) DESC
Si vous souhaitez compter les "posts de blog concernés" " seulement, je pense que c'est comme ça:
SELECT
t.id tag_id,
t.name tag_name,
COUNT(DISTINCT COALESCE(x.blog_post_id, c.blog_post_id)) tag_count
FROM
Tags t
INNER JOIN Taggings x ON x.tag_id = t.id
LEFT JOIN BlogComments c ON c.id = x.blog_comment_id
GROUP BY
t.id,
t.name
ORDER BY
COUNT(DISTINCT COALESCE(x.blog_post_id, c.blog_post_id)) DESC
Autres conseils
Il se peut que quelque chose d’évident me manque, mais comme vous avez " si quelqu'un associe un commentaire à un message, il remplit blog_post_id et blog_comment_id " ;, le code SQL suivant devrait faire l'affaire. Je suppose ici que Tags.name sera unique ici.
SELECT MIN(ts.tag_id), t.name, COUNT(ts.blog_post_id) as rank
FROM Taggings ts
INNER JOIN Tags t ON ts.tag_id = t.id
GROUP BY t.name
ORDER BY COUNT(ts.blog_post_id) DESC
J'espère que c'est ce que vous cherchez.
Je n'ai pas essayé, mais qu'en est-il de quelque chose comme ça?:
select t.Id,
t.Name,
count(*)
from Taggings tings
inner join Tags t
on (t.id = tings.blog_post_id or t.id = tings.blog_comment_id)
group by t.Id, t.Name
order by count(*) desc