MySQL 最长前缀匹配 100 万条记录与 3000 种可能性

Posted

技术标签:

【中文标题】MySQL 最长前缀匹配 100 万条记录与 3000 种可能性【英文标题】:MySQL longest prefix matching on 1 million records against 3000 possibilities 【发布时间】:2014-03-02 22:40:18 【问题描述】:

我有一个包含大约 100 万个电话号码的表格和另一个包含大约 3000 个 ISD 代码(国家代码)的表格。现在我想将电话号码与所有这些 ISD 代码进行最长前缀匹配。 例如,在 ISD 表中:

1     US
1808  US (Hawaii)

如果电话号码现在是 1223244223,它应该返回美国,但如果它是 1808322353,它应该返回美国(夏威夷)。 以最佳性能实现这一目标的最佳方法是什么?

这是我目前所拥有的。不幸的是,对性能不满意,我想避免使用功能:

DELIMITER $$

CREATE DEFINER=`root`@`localhost` FUNCTION `isd`(telnum varchar(32)) RETURNS int(4)
BEGIN
    RETURN (SELECT if(locate(isd, telnum)=1, (locate(isd, telnum)*length(isd)), 0) as score FROM tbl_ref_isd_v1 having score>0 order by score desc limit 1);
END

另外我还有这个不同的功能,好像快一点:

DELIMITER $$

CREATE DEFINER=`root`@`localhost` FUNCTION `isd_new`(telnum varchar(32)) RETURNS int(4)
BEGIN
    RETURN (
        select isd 
from test.tbl_ref_isd_v1 
where telnum like CONCAT(isd, '%') 
order by length desc LIMIT 1
    );
END

【问题讨论】:

【参考方案1】:

这是一个可以满足您要求的查询:

select pn.*, max(ic.code)
from (select pn.*, ic.code, len(ic.code)
      from PhoneNumbers pn join
           ISDCodes ic
           on pn.phonenumber like concat(ic.code, '%')
     ) pni
group by pn.phonenumber;

请注意,对于初始字符串,max() 有效,因为1808 大于1,依此类推。

编辑:

ISDCodes(code) 上建立索引。那么以下应该可以正常工作:

select pn.*, coalesce(ic5.code, ic4.code, ic3.code, ic2.code, ic1.code) as code
from PhoneNumbers pn left outer join
     ISDCodes ic1
     on left(pn.phonenumber, 1) = ic1.code left outer join
     ISDCodes ic2
     on left(pn.phonenumber, 2) = ic2.code left outer join
     ISDCodes ic3
     on left(pn.phonenumber, 3) = ic3.code left outer join
     ISDCodes ic4
     on left(pn.phonenumber, 4) = ic4.code left outer join
     ISDCodes ic5
     on left(pn.phonenumber, 5) = ic5.code;

您需要将连接添加到更长的ic.code

【讨论】:

您的逻辑是有效的,但速度很慢(在 180k 与 3700 isd 代码的小型记录集上 > 80 秒)。与需要 0,2s 的功能相比。不幸的是,该函数不会从 isd 表中返回整行(我更喜欢)。 第二个查询要快得多(也比函数快)。现在有没有办法独立于 ISDCode 长度?我有 1 到 9 位数字的代码。我添加了最多 9 个额外的连接,但我想知道我们是否可以以相同的性能使这个动态。对于我从 ISDCodes 表中获取的所有其他字段,我是否总是需要使用 coalesce 或者是否有更好的方法? @DavidLaroche 。 . .我认为您不能使这种动态化,并且仍然让查询使用使用基本 SQL 的索引。你可以使用prepare 语句来做到这一点。 我现在使用了 9 个单独的连接,这不应该经常改变,所以最后一切都很好而且很快。谢谢!

以上是关于MySQL 最长前缀匹配 100 万条记录与 3000 种可能性的主要内容,如果未能解决你的问题,请参考以下文章

最长前缀匹配(LPM)

一个 VPC 与使用最长前缀匹配的两个 VPC 具有对等关系

联合索引的最左前缀匹配原则

MySQL - 获取最长字符串匹配的记录

mysql 如何实现 count 超过1000条记录 就返回 1000

[AC自动机]玄武密码