Uma consulta SQL difícil: tag popularidade para modelos com associações complexas
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!
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