SQL查找最受欢迎的类别

时间:2009-06-27 01:45:54

标签: sql mysql

我的数据库(MySQL)中有3个表。

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文档有点神秘,我不太了解'AS'和'JOINS'。

6 个答案:

答案 0 :(得分:5)

试试这个。使用group by和类别的名称。我已按照您的指定注释掉了创建的at子句,如果您想使用它,可以取消注释。

 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的更多信息,请here is a tutorial

Here, too, is a tutorial on SQL aliases(AS子句)。事实上,在这个网站上有很多教程可以提供一系列不依赖于平台的不同SQL主题。

编辑按照评论修复,添加了abs功能,

答案 1 :(得分:1)

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;

编辑:好点安德鲁,我修复了查询

答案 2 :(得分:1)

这是在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,sum(v.value)作为VoteSum - 获取类别名称和投票总数

来自类别c内部联接项目我 - 加入层次结构:类别到项目

on c.category_id = i.category_id - on category_id

内部联合投票v - 项目投票

on i.item_id = v.item_id - on item_id

其中created_at介于dateadd(周,-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 ')

答案 3 :(得分:1)

在这种情况下你可以使用group by并删除连接。当使用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”允许你给出一个名字。

注意上面的得分,这给出了count(votes.item_id)得分的列名结果,因为它之前没有列名。如果要在查询的其余部分中使用其他名称调用某些内容,也可以使用此方法。

如果您取消了得分,它将返回为没有标题的列,无法通过名称访问它,只能通过数字。

<强> JOIN

连接将两个表合并为1个临时表并返回此表。有内,外,左,右和交叉连接。每个都有自己的优点,但都有同样的缓慢问题。查看子查询以替换大多数联接。

您还希望避免使用Select *,列出您需要的所有内容

解决这些问题的最佳方法是运行它们并查看它们返回的内容并阅读它们应该执行的操作w3Schools Joins

答案 4 :(得分:1)

1)我假设categories表也有category_id字段,items表有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”表,这意味着类别表。
  • JOINS是必不可少的。开始阅读。
  • 在我的解决方案中,我使用了mySQL原生的DATE_SUB。我不知道有多少其他数据库使用该功能,但所有数据库都有类似的功能。
  • 我的查询为您提供了最受欢迎的十大类别。请注意,LIMIT N子句是您在mySQL中执行此操作的方式。在SQLServer中,在SELECT关键字后使用TOP N.在Oracle中,它以另一种方式完成。
  • 我拿了票的绝对值,因为你说包括上涨或下票。
  • ORDER BY 2 DESC表示按选择中的第二列降序排序。你可以在这里使用实际的表达式,但这更像是打字。
  • GROUP BY是必要的。如果使用任何聚合函数,则每个不是常量或用SUM,COUNT,MAX等聚合的列必须出现在GROUP BY子句中。

答案 5 :(得分:1)

这是我最终使用的(@ 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得分”,但这不起作用。如果有人知道如何发表评论。谢谢大家!