Mysql返回最后一个活动早于X天的ID

问题描述 投票:1回答:3

我有一个日志表,其中存储了每个用户活动。

用户活动表(约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查询中的所有东西?

php mysql performance
3个回答
2
投票

您可以使用以下查询来获取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)
)

1
投票

有没有一种干净的方法来获取一个SQL查询中的所有东西?

是的,您可以使用以下查询在一个步骤中更新Users表:

UPDATE `Users` SET `Active` = EXISTS(
    SELECT * from `UserActivityTable ` WHERE
        `UserActivityTable `.`userID` = `Users`.`id` AND
        `timestamp`>DATE_SUB( NOW(), INTERVAL 30 DAY )
    )

EXISTS语句返回10,具体取决于在过去30天内是否存在用户活动中的至少一条记录。因此,Active字段已针对每个用户正确更新为10


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的记录。

随着事件表随着时间的推移无限增长,您应该考虑定期删除旧条目或将它们移动到单独的表/转储文件中。


0
投票

不要这样做。

在数据库中拥有冗余信息是不合适的。 (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是针对那个吗?请将该要求折叠到问题中,否则我(和其他人)不一定帮助您。

© www.soinside.com 2019 - 2024. All rights reserved.