Una consulta SQL difícil: la popularidad de la etiqueta para modelos con asociaciones complejas

StackOverflow https://stackoverflow.com/questions/620788

  •  05-07-2019
  •  | 
  •  

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!

¿Fue útil?

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top