我有一个日志表,其中存储了每个用户活动。
用户活动表(约1500万条记录)
id userID category value timestamp
1 2 Visit homepage 2018-02-21 13:13:54
1 2 Visit page2 2018-02-18 13:13:45
1 2 Visit page1 2018-02-15 13:13:30
1 3 Visit homepage 2018-02-01 13:13:12
使用SQL查询我需要获取所有用户ID,如果用户设置为“活动”,则最后一个活动的时间早于X天(假设为30)
用户(15k用户)
id Groups Active Name Mails ...
2 Customer 1 Hans
3 Customer 0 Wurst
如果我得到所有活跃的用户(大约5k)并且尝试到达最后一个活动我遇到超时(我认为查询不是高性能)如果我将它限制为5则没有问题。
我尝试了什么。
1选择所有活动的用户,而不是使用foreach函数来获取最后一个活动,如果超过30天我将其写入新数组中,最后我使用该数组将用户表中的活动设置为false。
直到最后2-3个月,它还不错,但现在我们有很多新用户,功能无法处理它。
有没有一种干净的方法来获取一个SQL查询中的所有东西?
您可以使用以下查询来获取Users
:
SELECT `userID`, MAX(`timestamp`) AS lastActive FROM `UserActivityTable`
WHERE `userID` IN (
SELECT `id` FROM `Users` WHERE `Active` = 1
) GROUP BY `userID` HAVING lastActive < DATE_SUB(NOW(), INTERVAL 30 DAY)
索引
PRIMARY KEY
表上使用Users
索引。FOREIGN KEY
表上使用UserActivityTable
索引。timestamp
列上创建列索引。您可以使用以下命令在INDEX
列上创建timestamp
:
CREATE INDEX index_timestamp ON `UserActivityTable` (`timestamp`);
您还可以在UPDATE
表上使用单个查询来active
Users
状态:
UPDATE `Users` SET `active` = EXISTS (
SELECT `userID` FROM `UserActivityTable` WHERE `UserActivityTable`.`userID` = `Users`.`id` GROUP BY `UserActivityTable`.`userID` HAVING MAX(`UserActivityTable`.`timestamp`) > DATE_SUB(NOW(), INTERVAL 30 DAY)
)
有没有一种干净的方法来获取一个SQL查询中的所有东西?
是的,您可以使用以下查询在一个步骤中更新Users
表:
UPDATE `Users` SET `Active` = EXISTS(
SELECT * from `UserActivityTable ` WHERE
`UserActivityTable `.`userID` = `Users`.`id` AND
`timestamp`>DATE_SUB( NOW(), INTERVAL 30 DAY )
)
EXISTS
语句返回1
或0
,具体取决于在过去30天内是否存在用户活动中的至少一条记录。因此,Active
字段已针对每个用户正确更新为1
或0
。
Mysql返回最后一个活动早于X天的ID
如果您只想要具有活动的用户ID列表:
SELECT `Users`.`id` WHERE EXISTS(
SELECT * from `UserActivityTable ` WHERE
`UserActivityTable `.`userID` = `Users`.`id` AND
`timestamp`>DATE_SUB( NOW(), INTERVAL 30 DAY )
) = 1
为了获得良好的表现(至少),必须将timestamp
字段编入索引。
边注
你已经打了15M的记录。
随着事件表随着时间的推移无限增长,您应该考虑定期删除旧条目或将它们移动到单独的表/转储文件中。
不要这样做。
在数据库中拥有冗余信息是不合适的。 (active
是多余的,因为它可以通过对UserActivityTable
的查询来发现。)
好的,你需要更多的性能,所以你要设置这个标志。我认为这不是一次性任务,但需要每天更新?或者是什么?我问这个是因为如果'用户'在你运行active=0
之后做了什么,并且再次运行它之前,UPDATE
将会出错!
让我们解决这个错误,然后发现我们在这个过程中非常快地制作了UPDATE
。
修复该bug的“唯一”方法是动态地进入UserActivityTable
。但是,我们可以做到这么便宜,以便“实时”完成。
FROM Users
WHERE EXISTS ( SELECT * FROM UserActivityTable
WHERE userID = x.userID
AND timestamp > NOW() - INTERVAL 30 DAY ) -- == "active"
UserActivityTable needs INDEX(userID, timestamp)
哎呀!我只是避免了对active
专栏的需要。
你的一条评论提到清除“旧的,不活跃的”用户? UPDATE
是针对那个吗?请将该要求折叠到问题中,否则我(和其他人)不一定帮助您。