我很确定我们不能将LIMIT条款用于我想做的事情 - 所以想找出是否还有其他方法可以实现这一点。
我有一张表,用于捕获哪个用户访问了哪个商店。每次用户访问商店时,都会在此表中插入一行。
部分字段是
现在我想要的是 - 对于一组给定的商店,找到最多访问商店的前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个商店,但想知道是否有更好的方法
答案 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)
...
答案 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%标准化,但这应该不是问题。