Сложный запрос SQL: популярность тегов для моделей со сложными ассоциациями - PullRequest
1 голос
/ 07 марта 2009

Я даже не уверен, что это можно сделать эффективно, но вот моя проблема:

Я пишу то, что по сути является движком блога, где можно пометить сообщение в блоге и все ответы на каждое сообщение в блоге.

Итак, я мог бы иметь запись в блоге с тегом «стек» и ответ на этот пост с тегом «переполнение».

Прямо сейчас я пытаюсь создать список самых популярных тегов, когда пользователь нажимает на специальную страницу в моем приложении. Он должен возвращать не только n самых популярных тегов по убыванию количества сообщений в блоге, но также количество сообщений в блогах, связанных с каждым тегом, , даже если ответ в этом сообщении, но не само сообщение помечен этим тегом .

Таким образом, если BlogPost A помечен как «foo», а ответ в BlogPost B помечен как «foo», общее резюме популярных тегов должно учитываться как всего два сообщения в блоге, хотя BlogPost B технически не помечен .

Вот описание таблиц / полей, которые могут иметь отношение:

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

Существует некоторая денормализация в Taggings для удобства. Если кто-то помечает BlogPost, он заполняет поле blog_post_id, а blog_comment_id остается NULL. Если кто-то помечает комментарий к сообщению, он заполняет как blog_post_id, так и blog_comment_id.

Есть ли способ вернуть отсортированный список самых популярных тегов в одном или нескольких запросах SQL? Я думаю, что мне может понадобиться просто запускать вычислительно-дорогостоящий скрипт каждые несколько минут в задании cron и выводить кэшированный вывод вместо того, чтобы запускать его каждый раз, когда кто-то попадает на страницу ...

Спасибо!

Ответы [ 3 ]

1 голос
/ 07 марта 2009

Пока я не вижу ничего сложного в вашем запросе:

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

Если вы хотите считать только "затронутые посты в блоге", я думаю, что так:

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
0 голосов
/ 07 марта 2009

Я не пробовал, но как насчет этого?

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
0 голосов
/ 07 марта 2009

Возможно, я упускаю что-то очевидное, но, поскольку у вас есть «Если кто-то помечает комментарий к сообщению, он заполняет оба blog_post_id и blog_comment_id», следующий sql должен помочь. Я предполагаю, что Tags.name здесь будет уникальным.

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

Надеюсь, это то, что вы ищете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...