Una consulta SQL difícil: la popularidad de la etiqueta para modelos con asociaciones complejas
Pregunta
Ni siquiera estoy seguro de que esto sea posible de manera eficiente, pero aquí está mi problema:
Estoy escribiendo lo que es esencialmente un motor de blog donde se puede etiquetar una publicación de blog y todas las respuestas a cada publicación de blog.
Entonces, podría tener una publicación de blog etiquetada como "stack" y una respuesta a esa publicación etiquetada como "overflow".
En este momento, estoy tratando de generar una lista de las etiquetas más populares cuando un usuario accede a una página especial en mi aplicación. Debe devolver no solo las n etiquetas más populares por el número descendente de publicaciones de blog, sino también la cantidad de publicaciones de blog asociadas a cada etiqueta, incluso si una respuesta en esa publicación pero no la publicación en sí está etiquetada con esa etiqueta .
Por lo tanto, si BlogPost A está etiquetado con " foo " ;, y una respuesta en BlogPost B está etiquetada con " foo " técnicamente etiquetado.
Aquí hay una descripción de las tablas / campos que pueden ser relevantes:
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
Hay algo de desnormalización en Taggings por conveniencia. Si alguien etiqueta BlogPost, rellena el campo blog_post_id y blog_comment_id permanece NULL. Si alguien etiqueta un comentario en una publicación, completa tanto blog_post_id como blog_comment_id.
¿Hay alguna forma de devolver una lista ordenada de las etiquetas más populares en una o varias consultas SQL? Estoy pensando que podría necesitar ejecutar un script computacionalmente costoso cada pocos minutos en un trabajo cron y representar el resultado en caché en lugar de ejecutarlo cada vez que alguien llega a la página ...
¡Gracias!
Solución
Hasta ahora no veo nada complicado en su solicitud:
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 desea contar " publicaciones de blog afectadas " solo, creo que esa es la forma:
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
Otros consejos
Es posible que me falte algo obvio, pero como tienes " Si alguien etiqueta un comentario en una publicación, rellena tanto blog_post_id como blog_comment_id " ;, el siguiente sql debería hacer el truco. Asumo aquí que Tags.name aquí será único.
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
Espero que eso sea lo que estás buscando.
No lo intenté, pero ¿qué pasa con algo como esto ?:
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