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

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

  •  05-07-2019
  •  | 
  •  

Вопрос

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

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

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

Прямо сейчас я пытаюсь создать список самых популярных тегов, когда пользователь попадает на специальную страницу в моем приложении.Он должен возвращать не только 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

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

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

Спасибо!

Это было полезно?

Решение

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

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

Другие советы

Возможно, я упускаю что-то очевидное, но поскольку у вас есть «Если кто-то помечает комментарий к сообщению, он заполняет как 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

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

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

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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top