【问题标题】:MySQL: Detect existence of at least 1 record in large joined tableMySQL:检测大型连接表中至少存在 1 条记录
【发布时间】:2017-06-02 11:37:26
【问题描述】:

我有两张桌子:

用户(ID、姓名)

user_activities(id、user_id、activity_id、created_at)

user_activities 表非常大,有超过 3 亿行。

我正在尝试检测哪些用户在给定日期范围内进行了任何活动。换句话说,用户表上的行,其中一个连接行存在于 user_activities 表上某个 created_at 范围之间。

我可以使用 INNER JOIN、GROUP BY 和 WHERE 子句来做到这一点,但查询会运行很长时间,因为我相信它会在我的日期范围内命中所有 user_activities 行。

我真的不在乎“有多少”活动,只要他们有超过零个。所以我正在分组以获得计数(例如 210 个活动),而实际上我可以在找到 1 个后停止。

有没有比将所有 user_activity 行分组来计数更有效的方法?

有关信息,这是当前查询,它工作正常但需要很长时间:

SELECT u.id, u.name, COUNT(ua.id) AS activity_count
FROM users u
INNER JOIN user_activity ua ON u.id=ua.user_id
WHERE ua.created_at > '2017-01-01' AND ua.created_at < '2017-03-01'
GROUP BY u.id
HAVING activity_count > 0;

提前致谢!

【问题讨论】:

  • 这里不需要 HAVING,没有活动的用户不会被包括在内。 (即 activity_count 将始终大于 0。)
  • 那么我也可以删除 COUNT(ua.id) 吗?
  • 您是否需要计算每个用户的活动,还是知道用户处于活动状态(即在日期范围内至少有一项活动)就足够了?
  • 是的,不需要 - 只要您只是想知道是否有任何活动。
  • 至少存在 1 个活动,我不需要计算总共有多少。我认为 Gordon 在下面的第二个答案是进行此存在检查,所以我会尝试。

标签: mysql sql join


【解决方案1】:

你可以试试这个版本:

SELECT u.id, u.name,
       (SELECT COUNT(*)
        FROM user_activity ua 
        WHERE u.id = ua.user_id AND
              ua.created_at > '2017-01-01' AND
              ua.created_at < '2017-03-01'
       ) as activity_count
FROM users u
HAVING activity_count > 0;

为了提高性能,您需要在user_activity(user_id, created_at) 上建立索引。

编辑:

如果你只是想要存在,那么使用相同的索引,这应该会快得多:

SELECT u.id, u.name
FROM users u
WHERE EXISTS (SELECT 1
              FROM user_activity ua 
              WHERE u.id = ua.user_id AND
                    ua.created_at > '2017-01-01' AND
                    ua.created_at < '2017-03-01'
              );

虽然您的查询会进行复杂的处理,然后聚合一堆数据,但这应该扫描users 表,并在索引中查找用户是否存在适当的活动。

【讨论】:

  • 非常感谢 Gordon,您的第二个查询正是我想要的!
  • 看到你在预测分析方面的一些工作,我想你可能会喜欢这个。麻省理工学院发布了一些关于人工智能的讲座。开始有点慢,但会越来越好。 openculture.com/2017/05/… ...
  • @JohnCappelletti。 . .作为一名本科生,我在麻省理工学院学习了 Patrick Winston 的 AI 课程。我不会在公共论坛上给我的 cmets ;)
  • @GordonLinoff 太有趣了。我实际上在麻省理工学院任教。我儿子就读于夏季机器人学院(12 岁)。老师病了,所以我接了三天的课。从来没有足够的勇气把它写在我的简历上。
【解决方案2】:

使用EXISTS 子句,以便DBMS 认为在给定日期范围内为每个用户找到一条记录就足够了。

SELECT id, name
FROM users u
where exists
(
  select *
  from user_activity ua 
  where ua.user_id = u.id
  and ua.created_at > '2017-01-01' AND ua.created_at < '2017-03-01'
);

有了这个索引:

create index idx on user_activity(user_id, created_at);

【讨论】:

    【解决方案3】:

    获取在给定日期范围内完成活动的用户

    select u.id, u.name  from users u
        where exists ( SELECT 1  FROM user_activity ua 
            where ua.user_id = u.id 
            and ua.created_at > '2017-01-01' AND
                  ua.created_at < '2017-03-01')
    

    为 user_activity(created_at) 创建索引

    【讨论】:

      【解决方案4】:

      如果只是为了测试存在,那么:

      SELECT EXISTS(
          SELECT u.id
              FROM user_activity AS ua 
              WHERE u.id = ua.user_id 
                  AND ua.created_at > '2017-01-01' 
                  AND ua.created_at < '2017-03-01'
      ) AS ret
      

      这将简单地返回列 ret = 1 如果至少一行查询满足给定条件,否则返回列 ret = 0

      【讨论】:

        猜你喜欢
        • 2023-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-09
        • 2018-11-09
        • 1970-01-01
        • 1970-01-01
        • 2013-08-23
        相关资源
        最近更新 更多