MySQL统计一段时间内共享3列中至少1列的条目数


MySQL count a number of entries within time period sharing at least 1 of 3 columns

我有一个记录无效用户登录尝试的表。每次进行无效尝试时,用户名、用户IP、用户电子邮件和时间/日期都会存储在数据库中。

我想做的是检查在任何24小时内,同一用户是否有超过X次无效尝试。但是,用户可以随时更改电子邮件、用户名或IP。因此,我需要检查这3个字段中的任何一个是否有共同点。

例如:

  • 用户ID:1;IP:1.1.1.1;电子邮件:test@test.com
  • 用户ID:2;IP:1.1.1.1;电子邮件:test2@test.com
  • 用户ID:1;IP:1.1.1.2;电子邮件:test3@test.com
  • 用户ID:4;IP:1.1.1.4;电子邮件:test@test.com
  • 用户ID:5;IP:1.1.1.4;电子邮件:test5@test.com

所有这些都将匹配为同一用户,因为它们共享用户ID、IP或电子邮件。然后我需要输出所有的用户ID、IP和电子邮件,这样我就可以禁止另一个表中符合这些条件的任何用户。

您所拥有的是记录之间的连接图,其中的边是电子邮件、用户名和IP。你需要遍历这个图来找到连通的子图。这很难。例如,在您的示例中,id2和id2是连接的,但它们没有共同的字段。

所以,你需要一个走图算法。MySQL没有直接在SQL中支持此类算法的构造。您可以编写一个存储过程来查找这样的组,但这不是用一条SQL语句就能完成的。

编辑:

当我以前遇到这个问题时,我使用了SQL,其中包含重复的update语句。其想法是为每个记录分配遇到的最低userid。

create table tgroup as
    select t.*, id as grpid
    from table t;
update tgroup join
       (select email, min(id) as minid
        from tgroup t
        group by email
       ) tt
       on tt.email = tgroup.email and
          tt.minid < tgroup.id
    set tgroup.id = least(tt.minid, tgroup.id);
update tgroup join
       (select ip, min(id) as minid
        from tgroup t
        group by ip
       ) tt
       on tt.ip = tgroup.ip and
          tt.minid < tgroup.id
    set tgroup.id = least(tt.minid, tgroup.id);

然后,您必须重复此操作,直到没有更新为止。