微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

获取与某个帐户相关联的所有帐户通过 IP

如何解决获取与某个帐户相关联的所有帐户通过 IP

数据库

我有两张桌子:

他们的关系是:

  • account_ip.account 字段与 account.id 相关联。

account_ip 表的用途是

  • 跟踪每个帐户已成功登录的所有 IP。

帐号 IP 关联

约定:

  • 如果两个帐户在 account_ip 表中有一个共同的 IP,我们说这两个帐户是关联

例如:

account IP linking

在这种情况下,帐户 1213 相关联,因为它们有共同的 127.0.0.1,帐户 1314 相关联,因为它们有 127.0.0.4 的共同点:

12 13 14

查询

我可以运行一些queries,例如:

  • 级别 1:选择链接到给定帐户 ID 123 的所有帐户/IP
SELECT account.username,account_ip.* FROM account_ip 
INNER JOIN account ON account.id = account_ip.account
WHERE ip IN (
    SELECT ip FROM account_ip WHERE account = 123
);
  • 第 2 级:选择链接到与给定帐户 ID 123 关联的所有帐户的所有帐户/IP
SELECT account.username,account_ip.* FROM account_ip INNER JOIN account ON account.id = account_ip.account
WHERE ip IN (
    SELECT ip FROM account_ip WHERE account IN (
        SELECT account FROM account_ip WHERE ip IN (
            SELECT ip FROM account_ip WHERE account = 123
        )
    )
);

但最后一个只持续到 2 个链接级别。

问题

给定一个帐户 ID,我如何才能在任何链接级别获取与该帐户相关的所有帐户/IP?

例如,如果我有这个链接关系:

12 13 14 15 16

给定帐户 12,我想要一个查询,提供 12、13、14、15 和 16 的所有帐户和 IP。

解决方法

这是一个图遍历问题。 . .这需要递归 CTE。有两个元素很棘手。一种方法是:

with recursive cte as (
      select account,ip,cast(ip as char(1000)) as visited_ips,cast(account as char(1000)) as visited_accounts
      from accounts
      where account = 12
      union all
      select a.account,a.ip,concat_ws(',',cte.visited_ips,a.ip),cte.visited_accounts,a.account)
      from cte join
           accounts a
           on a.ip = cte.ip or a.account = cte.account
      where ((a.account = cte.account and find_in_set(a.ip,cte.visited_ips) = 0) or
             (a.ip = cte.ip and find_in_set(a.account,cte.visited_accounts) = 0) ) 
     )
select distinct ip
from cte;

Here 是一个 dbfiddle。

,

正如 Gordon Linoff 所指出的,这需要递归 CTE。但是,CTE 在 MySQL 8+ 上可用。如果您的数据库支持 CTE,他的解决方案是一种可行的方法。

话虽如此,我已经设法创建了一个存储过程来获取帐户和 IP。

限制是:

  • 它使用临时表 - 脚本不能在同一个会话中并行运行。
  • 未针对性能进行优化
DROP PROCEDURE if EXISTS find_linked_accounts;

DELIMITER //
CREATE PROCEDURE find_linked_accounts(account_id INT)
BEGIN
    
    SET @start_account_id = account_id;
    SET @last_data_count = 0;
    SET @data_count = 0;
    
        
    DROP TEMPORARY TABLE if exists linked_accounts;
    DROP TEMPORARY TABLE if exists temp_linked_accounts;
    
    CREATE TEMPORARY TABLE linked_accounts(
        `account` INT(10) UNSIGNED NOT NULL,`ip` VARCHAR(15) NOT NULL,PRIMARY KEY(`account`,`ip`)
    );
    
    CREATE TEMPORARY TABLE temp_linked_accounts(
        `account` INT(10) UNSIGNED NOT NULL,`ip`)
    );
    
    INSERT INTO  linked_accounts
    SELECT account,ip 
    FROM account_ip
    WHERE account_ip.account = @start_account_id;
    
    REPEAT
        SET @last_data_count = @data_count;
        # insert all accounts connected to the previously found accounts in the temp_linked_accounts table
        INSERT INTO temp_linked_accounts
        SELECT distinct aip.account,aip.ip 
        FROM account_ip aip
        INNER JOIN linked_accounts la ON la.account = aip.account OR la.ip = aip.ip;
        
        INSERT INTO linked_accounts
        SELECT account,ip 
        FROM temp_linked_accounts
        ON DUPLICATE KEY UPDATE linked_accounts.account=linked_accounts.account;
        
        SET @data_count = (SELECT COUNT(account) FROM linked_accounts);
        DELETE FROM temp_linked_accounts;
        UNTIL @data_count = @last_data_count
    END REPEAT;
    
    SELECT * FROM linked_accounts;
    
    DROP TEMPORARY TABLE if exists linked_accounts;
    DROP TEMPORARY TABLE if exists temp_linked_accounts;
END //

DELIMITER ;

要调用存储过程,请执行 call find_linked_accounts(account_id); - 用整数值替换 account_id,例如call find_linked_accounts(12);

此脚本已在最新的 AzerothCore 数据库(截至撰写本文时)以及问题中提供的 SQL 脚本上进行了测试。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。