Uma consulta SQL difícil: tag popularidade para modelos com associações complexas

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

  •  05-07-2019
  •  | 
  •  

Pergunta

Eu estou nem mesmo certeza de que isso é possível fazer de forma eficiente, mas aqui está o meu problema:

Eu estou escrevendo o que é essencialmente um mecanismo de blog onde um post do blog e todas as respostas a cada post pode marcados.

Então, eu poderia ter um blog com a tag "pilha", e uma resposta a esse post com a tag "overflow".

Agora, eu estou tentando gerar uma lista dos mais marcas populares quando um usuário acessa uma página especial em meu aplicativo. Ele deve retornar não apenas as tags n mais populares descendo número de posts, mas também o número de posts associados a cada tag, mesmo que uma resposta nesse post mas não o próprio posto é marcado com a tag .

Então, se BlogPost A é marcado com "foo", e uma resposta na BlogPost B é marcado com "foo", o resumo tag populares deve contar isso como dois posts no total, embora BlogPost B não é tecnicamente marcado .

Aqui está uma descrição das tabelas / campos que podem 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

Há alguns desnormalização em Taggings por uma questão de conveniência. Se alguém marcar BlogPost, ele preenche o campo blog_post_id e restos blog_comment_id NULL. Se alguém marca um comentário a um post, ele preenche ambos blog_post_id e blog_comment_id.

Existe alguma maneira para retornar uma lista ordenada dos mais marcas populares em uma ou várias consultas SQL? Eu estou pensando que eu poderia precisa apenas executar um script computacionalmente caro a cada poucos minutos em um trabalho cron e tornar a saída em cache em vez de correr esse alguém cada vez que atinge a página ...

Obrigado!

Foi útil?

Solução

Até agora não vejo nada complicado em seu pedido:

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

Se você deseja contar "posts afetadas" única, eu acho que essa é a maneira:

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

Outras dicas

eu possa estar faltando alguma coisa óbvia, mas desde que você tem "Se alguém marca um comentário a um post, ele preenche ambos blog_post_id e blog_comment_id", o seguinte sql deve fazer o truque. Eu estou assumindo aqui que Tags.name aqui 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

Hope isso é o que a sua procura.

Eu não tentei, mas o que dizer algo como isto:?

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top