SQL найти самую популярную категорию - PullRequest
4 голосов
/ 27 июня 2009

У меня есть 3 таблицы в моей БД (MySQL).

categories (name:string)
items (name:string, category_id:int)
votes (value:int, item_id:int, created_at:datetime)

Таким образом, в категории много предметов, а у предмета много голосов.

Я хочу написать запрос, чтобы получить наиболее популярные категории, что означает получение категорий, элементы которых набрали наибольшее количество голосов (как вверх, так и вниз) за последнюю неделю.

Я начал пытаться сделать что-то попроще, просто получить популярные предметы, но на самом деле я просто догадываюсь, и это не работает.

SELECT *, COUNT(votes.item_id) AS score
FROM items
JOIN votes USING(item_id)
WHERE votes.created_at > #{1.week.ago}
ORDER BY COUNT(votes.item_id) DESC LIMIT 5;

Я действительно не знаю, что я делаю, есть идеи? Кроме того, если кто-нибудь знает о хорошем написании более продвинутых выборов, как это, я хотел бы прочитать это. Документация по MySQL немного загадочна, и я не совсем понимаю, «КАК» и «СОЕДИНЯЕТСЯ».

Ответы [ 6 ]

5 голосов
/ 27 июня 2009

попробуйте это. использовать group by с названием категории. я закомментировал созданное в предложении, как вы указали, вы можете раскомментировать его, если хотите его использовать.

 SELECT c.name, SUM(ABS(v.item_id)) 
 FROM categories c,items i, votes v
 WHERE c.name = i.name
    AND i.item_id=v.item_id
    --AND v.created_at > #{1.week.ago}
 GROUP BY c.name 
 ORDER BY SUM(ABS(v.item_id)) DESC LIMIT 5;

вы заметите, что я не использовал ключевое слово JOIN, а вместо этого отфильтровал результаты запроса, используя только предложения WHERE, что может быть проще для понимания. если вы хотите узнать больше о JOIN, вот учебник .

Здесь также приведено руководство по псевдонимам SQL (предложение AS). на самом деле, на этом сайте есть еще куча учебников по различным темам SQL, которые не зависят от платформы.

edit: исправлено в соответствии с комментариями, добавлена ​​функция abs,

1 голос
/ 29 июня 2009

Это то, чем я в конечном итоге воспользовался (слегка измененная версия ответа @ akf). Другие ответы тоже были великолепны, но этот, как новичок, показался мне наиболее простым. Мне интересно, что он не использует никаких соединений? Не думал бы, что это возможно. Мне нравится, насколько это чисто и просто.

SELECT c.*, SUM(ABS(v.vote)) AS score
FROM categories c,items i, votes v
  WHERE c.id = i.category_id
  AND i.id = v.item_id
  AND v.created_at > '#{1.week.ago}'
GROUP BY c.id
ORDER BY score DESC LIMIT 5;

(Я забыл упомянуть в вопросе, что в каждой таблице есть столбец 'id', который вы можете увидеть здесь).

Суммирование абсолютного значения значения голоса в этом случае работает, поскольку есть положительные и отрицательные стороны, как правильно указали некоторые авторы, но я подумал, что было бы еще проще просто подсчитать количество записей голосов в базе данных. Я пробовал "COUNT (v) AS Score", но это не сработало. Если кто-нибудь знает, как, пожалуйста, оставьте комментарий. Спасибо всем!

1 голос
/ 27 июня 2009

1) Я предполагаю, что таблица категорий также имеет поле category_id, а таблица элементов имеет item_id, иначе вы не сможете связать таблицы. 2) ключевое слово AS часто является необязательным

SELECT c.name, SUM(ABS(v.value))
FROM categories c
INNER JOIN items i ON c.category_id = i.category_id
INNER JOIN votes v ON v.item_id = i.item_id
WHERE v.created_at BETWEEN DATE_SUB(NOW(), INTERVAL -7  DAYS) AND NOW()
GROUP BY c.name
ORDER BY 2 DESC
LIMIT 10
  • Ключевое слово AS предназначено для создания синонима имени таблицы. В большинстве случаев это просто сокращение, но если вы выполняете самостоятельные объединения, когда таблица соединяется с самим собой, вам необходимо различать их. И если две таблицы имеют одинаковое имя поля, вам нужно указать, какое поле таблицы вы используете, поэтому c.category_id происходит из таблицы "c", что означает таблицу категорий.
  • СОЕДИНЕНИЯ необходимы. Начните читать.
  • В моем решении я использовал DATE_SUB, который является родным для mySQL. Я не знаю, сколько других баз данных используют эту функцию, но все они имеют что-то похожее.
  • Мой запрос дает вам первую десятку категорий с наиболее популярными первыми. Обратите внимание, что предложение LIMIT N - это то, как вы делаете это в mySQL. В SQLServer используйте TOP N после ключевого слова SELECT. В Oracle это делается по-другому.
  • Я взял абсолютное значение голосов, потому что вы сказали включить или понизить число голосов.
  • ORDER BY 2 DESC означает сортировку по убыванию по второму столбцу в выборке. Вы можете использовать фактическое выражение здесь, но это больше печатать.
  • Требуется GROUP BY. Каждый столбец, который не является константой или агрегирован с помощью SUM, COUNT, MAX и т. Д., Должен присутствовать в предложении GROUP BY, если используются какие-либо агрегатные функции.
1 голос
/ 27 июня 2009

В этом случае вы можете использовать group by и удалить соединение. Я всегда облажался при использовании группы, но что-то вроде

SELECT COUNT(votes.item_ID) AS score, 
  (SELECT ItemTitle FROM items WHERE items.item_id = votes.item_id) as Title
FROM votes
WHERE votes.created_at > #{1.week.ago}
Group By Title
Order By score
Limit 5

AS

«как» позволяет вам дать что-то имя.

Обратите внимание, что над оценкой as выводится результат подсчета (voices.item_id) имени столбца оценки, поскольку у него ранее не было имени столбца. Вы также можете использовать это, если вы хотите вызвать что-то по другому имени в оставшейся части запроса.

Если вы удалите счет as, он вернется в виде столбца без заголовка и без доступа к нему по имени, только по номеру.

JOIN

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

Вам также следует избегать использования Select *, перечислить все, что вам нужно .

Лучший способ выяснить это - просто запустить их все, посмотреть, что они возвращают, и прочитать, что они должны делать Соединения w3Schools

1 голос
/ 27 июня 2009

Это в SQL Server ... но я думаю, что его легко конвертировать для парня из MySQL!

select top 5
    c.name as CategoryName, sum(v.value) as VoteSum
from categories c inner join items i
    on c.category_id = i.category_id
    inner join votes v
        on i.item_id = v.item_id
where created_at between dateadd(week,-1,getdate()) and getdate()
group by c.name
order by sum(v.value) desc

выберите топ 5 - количество категорий для отображения в расчете

c.name в качестве CategoryName, сумма (v.value) в качестве VoteSum - получить имя категории и сумму голосов

из категорий c элементами внутреннего соединения i - присоединить иерархию: категории к элементам

on c.category_id = i.category_id - on category_id

голосование за внутреннее объединение v - элементы для голосования

on i.item_id = v.item_id - on item_id

где create_at между dateadd (week, -1, getdate ()) и getdate () - указать диапазон дат для включения

группировать по c.name - группировать результаты по категориям

упорядочить по сумме (v.value) desc - упорядочить результаты по суммированному значению

Вот некоторые sql (из SQL Server) для запуска и запуска таблиц:

CREATE TABLE [categories](
    [category_id] [int] IDENTITY(1,1) NOT NULL,
    [name] [varchar](50) NOT NULL
)

CREATE TABLE [items](
[item_id] [int] IDENTITY(1,1) NOT NULL,
[category_id] [int] NOT NULL,
[name] [varchar](50)
)

CREATE TABLE [dbo].[votes](
[vote_id] [int] IDENTITY(1,1) NOT NULL,
[value] [int] NOT NULL,
[item_id] [int] NOT NULL,
[created_at] [datetime] NOT NULL
)

insert into categories (name) values (' asp.net ')
insert into categories (name) values (' c#  ')
insert into categories (name) values (' vb  ')
insert into categories (name) values (' sql ')
insert into categories (name) values (' html    ')
insert into categories (name) values (' javascript  ')

insert into items (category_id, name) values (  1   ,'  session handling    ')
insert into items (category_id, name) values (  1   ,'  mvc vs mvp  ')
insert into items (category_id, name) values (  1   ,'  code behind or no code behind   ')
insert into items (category_id, name) values (  2   ,'  LINQ?   ')
insert into items (category_id, name) values (  2   ,'  lamdas  ')
insert into items (category_id, name) values (  2   ,'  multi-threaded code ')
insert into items (category_id, name) values (  2   ,'  SOLID principles    ')
insert into items (category_id, name) values (  3   ,'  vb vs C#    ')
insert into items (category_id, name) values (  3   ,'  VB.NET over vb6 ')
insert into items (category_id, name) values (  4   ,'  CLR procedures or stored procedures ')
insert into items (category_id, name) values (  4   ,'  ORMs vs stored procedures and views ')
insert into items (category_id, name) values (  6   ,'  jquery instead of standard DOM  ')

insert into votes (value, item_id, created_at) values ( -1  ,   1   ,'  6/26/2009 18:59 ')
insert into votes (value, item_id, created_at) values ( 1   ,   1   ,'  6/26/2009 18:59 ')
insert into votes (value, item_id, created_at) values ( 3   ,   1   ,'  6/26/2009 18:59 ')
insert into votes (value, item_id, created_at) values ( 3   ,   1   ,'  6/26/2009 18:59 ')
insert into votes (value, item_id, created_at) values ( 2   ,   2   ,'  6/26/2009 18:59 ')
insert into votes (value, item_id, created_at) values ( 2   ,   2   ,'  6/26/2009 18:59 ')
insert into votes (value, item_id, created_at) values ( 4   ,   2   ,'  6/26/2009 18:59 ')
insert into votes (value, item_id, created_at) values ( -3  ,   2   ,'  6/26/2009 18:59 ')
insert into votes (value, item_id, created_at) values ( 2   ,   4   ,'  6/26/2009 19:00 ')
insert into votes (value, item_id, created_at) values ( 6   ,   4   ,'  6/26/2009 19:00 ')
insert into votes (value, item_id, created_at) values ( 3   ,   4   ,'  6/26/2009 19:00 ')
insert into votes (value, item_id, created_at) values ( 5   ,   4   ,'  6/26/2009 19:00 ')
insert into votes (value, item_id, created_at) values ( 8   ,   7   ,'  6/26/2009 19:00 ')
insert into votes (value, item_id, created_at) values ( 3   ,   6   ,'  6/26/2009 19:00 ')
insert into votes (value, item_id, created_at) values ( 8   ,   7   ,'  6/26/2009 19:01 ')
insert into votes (value, item_id, created_at) values ( 2   ,   5   ,'  6/26/2009 19:01 ')
1 голос
/ 27 июня 2009
SELECT c.name, sum(v.value) as cnt
 FROM categories c
 JOIN items i ON i.category_id = c.id
 JOIN votes v ON v.item_id = i.id
 WHERE v.created_at > #{1.week.ago}
 GROUP BY c.name 
 ORDER BY cnt DESC LIMIT 5;

Редактировать: хорошая точка зрения Андрей, я исправил запрос

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