Una query SQL difficile: popolarità dei tag per i modelli con associazioni complesse

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

  •  05-07-2019
  •  | 
  •  

Domanda

Non sono nemmeno sicuro che sia possibile farlo in modo efficiente, ma ecco il mio problema:

Sto scrivendo quello che è essenzialmente un motore di blog in cui un post di blog e tutte le risposte a ciascun post di blog possono essere taggati.

Quindi, potrei avere un post sul blog taggato " stack " ;, e una risposta a quel post taggato " overflow " ;.

In questo momento, sto cercando di generare un elenco dei tag più popolari quando un utente accede a una pagina speciale nella mia applicazione. Dovrebbe restituire non solo i n tag più popolari in base al numero decrescente di post di blog, ma anche il numero di post di blog associati a ciascun tag, anche se una risposta in quel post ma non il post stesso è taggata con quel tag .

Quindi, se BlogPost A è taggato con " foo " ;, e una risposta in BlogPost B è taggata con " foo " ;, il popolare sommario dei tag dovrebbe considerarlo come due post di blog in totale, anche se BlogPost B non lo è tecnicamente taggato.

Ecco una descrizione delle tabelle / campi che potrebbero essere rilevanti:

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

C'è qualche denormalizzazione in Taggings per motivi di convenienza. Se qualcuno tagga BlogPost, riempie il campo blog_post_id e blog_comment_id rimane NULL. Se qualcuno tagga un commento a un post, riempie sia blog_post_id che blog_comment_id.

Esiste un modo per restituire un elenco ordinato dei tag più popolari in una o più query SQL? Sto pensando che potrei aver bisogno di eseguire uno script costoso dal punto di vista computazionale ogni pochi minuti su un processo cron e rendere l'output memorizzato nella cache invece di eseguirlo ogni volta che qualcuno accede alla pagina ...

Grazie!

È stato utile?

Soluzione

Finora non vedo nulla di complicato nella tua richiesta:

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 desideri contare " post di blog interessati " solo, penso che sia così:

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

Altri suggerimenti

Forse mi manca qualcosa di ovvio, ma poiché hai " Se qualcuno tagga un commento ad un post, riempie sia blog_post_id che blog_comment_id " ;, il seguente sql dovrebbe fare il trucco. Suppongo che Tag.name qui sia unico.

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

Spero che sia quello che stai cercando.

Non ho provato, ma che dire di qualcosa del genere ?:

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top