Sql Query - 限制查询结果

时间:2010-03-08 16:56:11

标签: sql mysql

我很确定我们不能将LIMIT条款用于我想做的事情 - 所以想找出是否还有其他方法可以实现这一点。

我有一张表,用于捕获哪个用户访问了哪个商店。每次用户访问商店时,都会在此表中插入一行。

部分字段是

  • shopping_id(主键)
  • STORE_ID
  • USER_ID

现在我想要的是 - 对于一组给定的商店,找到最多访问商店的前5位用户。

我可以一次做1个商店:

select store_id,user_id,count(1) as visits 
from shopping 
where store_id = 60
group by user_id,store_id 
order by visits desc Limit 5

这将为我提供访问store_id = 60最多次数

的5位用户

我想要做的是提供10个store_id的列表,并且每个商店获取访问该商店最多次数的5个用户

select store_id,user_id,count(1) as visits 
from shopping 
where store_id in  (60,61,62,63,64,65,66)
group by user_id,store_id 
order by visits desc Limit 5
这不起作用,因为最后的限制将为每个商店仅返回5行而不是5行。

关于如何实现这一目标的任何想法。我总是可以写一个循环并一次传递1个商店,但想知道是否有更好的方法

5 个答案:

答案 0 :(得分:3)

使用两个用户变量并计算相同的连续store_id,您可以将<= 5替换为您想要的任何限制

SELECT a.*
FROM (
 SELECT store_id, user_id, count(1) as visits 
 FROM shopping
 WHERE store_id IN (60,61,62,63,64,65,66)
 GROUP BY store_id, user_id
 ORDER BY store_id, visits desc, user_id
) a,
(SELECT @prev:=-1, @count:=1) b
WHERE
 CASE WHEN @prev<>a.store_id THEN
   CASE WHEN @prev:=a.store_id THEN
    @count:=1
   END
 ELSE
   @count:=@count+1
 END <= 5

按要求编辑一些解释:

第一个子查询(a)是对数据进行分组和排序的子查询,因此您将获得如下数据:

store_id | user_id | visits
---------+---------+-------
 60           1       5
 60           2       3
 60           3       1
 61           2       4
 61           3       2

第二个子查询(b)用-1和@prev初始化用户变量@count

然后我们从子查询中选择所有数据(a)验证case中的条件。

  • 验证我们看到的先前store_id(@prev)与当前store_id不同。 由于第一个@prev等于-1,因此没有任何内容与当前store_id匹配,因此条件<>为真我们输入的是第二个用于更改值@prev的情况使用当前的store_id。这是技巧,所以我可以在相同条件下更改两个用户变量@count@prev

  • 如果前一个store_id等于@prev,只需递增@count变量。

  • 我们检查计数是否在我们想要的值范围内,以便<= 5

所以我们的测试数据是:

step | @prev | @count | store_id | user_id | visits
-----+-------+--------+----------+---------+-------
  0      -1      1    
  1      60      1        60          1        5 
  2      60      2        60          2        3
  3      60      3        60          3        1
  4      61      1        61          2        4
  5      61      2        61          3        2   

答案 1 :(得分:2)

此处主要关注的是查询数据库的次数。 如果您从脚本中多次查询。它只是浪费资源,必须避免。 也就是说,您不能通过递增某个值来运行循环来多次运行SQL。在你的情况下60到61等等。

解决方案1: 创建一个视图 这是解决方案

CREATE VIEW myView AS
select store_id,user_id,count(1) as visits 
from shopping 
where store_id = 60
group by user_id,store_id 
order by visits desc Limit 5
UNION
select store_id,user_id,count(1) as visits 
from shopping 
where store_id = 61
group by user_id,store_id 
order by visits desc Limit 5
UNION
select store_id,user_id,count(1) as visits 
from shopping 
where store_id = 62
group by user_id,store_id
order by visits desc Limit 5 

现在使用

SELECT * from MyView

这是有限的,因为你不能使它动态。 如果您需要60到100而不是60到66,该怎么办?

解决方案2: 使用程序。 我不会深入研究如何写一个程序,因为它深夜,我睡着了。 :) 那么,程序必须接受两个值第一个初始数(60)和第二个计数(6) 在过程内部创建一个临时表(游标)来存储数据,然后从初始数到计数次运行循环 在你的情况下从60到66 在循环内部写下所需的脚本用循环变量替换60。

select store_id,user_id,count(1) as visits 
from shopping 
where store_id = 60
group by user_id,store_id 
order by visits desc Limit 5

并将结果附加到临时表(光标)。

希望这能解决您的问题。 对不起,我无法给你代码。如果你仍然需要它,请给我发消息。我第二天早上醒来时会把它给你。

答案 2 :(得分:1)

UNION可能就是你想要的。

-- fist store
(select store_id,user_id,count(1) as visits 
from shopping 
where store_id = 60
group by user_id,store_id 
order by visits desc Limit 5)
UNION ALL
-- second store
(select store_id,user_id,count(1) as visits 
from shopping 
where store_id = 61
group by user_id,store_id 
order by visits desc Limit 5)
...

http://dev.mysql.com/doc/refman/5.0/en/union.html

答案 3 :(得分:1)

如果您不保存有关用户何时访问商店或类似内容的数据,您可以在每次用户访问商店时更新该表,而不是添加新行。

这样的事情:

INSERT INTO `user_store` (`user_id`, `store_id`, `visits`) VALUES ('USER', 'SHOP', 1)
ON DUPLICATE KEY UPDATE `visits` = `visits` + 1

但我认为这不起作用,因为user_id和store_id都不是唯一的。您必须添加一个唯一的主键,如:user#store或其他。

另一种意见是在每次向现有表添加新行时,在包含ID,user_id,store_id,访问和增量访问的单独表中保存此数据(用户在商店中的频率)。< / p>

要获得Top5,您可以使用:

SELECT `visits`, `user_id` FROM `user_store_times` WHERE `store_id`=10 ORDER BY `visits` DESC LIMIT 5

答案 4 :(得分:0)

最简单的方法是发出10个单独的查询,每个商店一个查询。如果您使用参数化查询(例如使用PDO in PHP),这将非常快,因为查询将是部分编译的。

如果仍然证明资源过于密集,那么另一种解决方案是将结果缓存到商店表中 - 即添加一个字段,将每个商店的前5个用户列为简单的逗号分隔列表。它确实意味着您的数据库不会100%标准化,但这应该不是问题。