前缀最佳匹配 MySQL

Posted

技术标签:

【中文标题】前缀最佳匹配 MySQL【英文标题】:Prefix best match MySQL 【发布时间】:2019-01-11 10:52:09 【问题描述】:

这就是我的情况。 我有 4 个表:记录、提供者、routing_domain、域。

域:ID、名称(类似于“example.com”) 提供者:ID、名称(类似于“TLC”) 记录:phone_number (varchar)、provider_id(外键 供应商) routing_domain:provider_id(提供者的外键),domain_id (域的外键)和前缀 (varchar)。

表格示例:

mysql> select id,name from domains;
+----+-----------------------+
| id | name                  |
+----+-----------------------+
|  1 | e164.arpa             |
|  3 | example.com           |
|  0 | localhost.localdomain |
|  4 | luigi.it              |
|  2 | tim.it                |
+----+-----------------------+

mysql> select id,name from providers where id in (9,10);
+----+----------+
| id | name     |
+----+----------+
|  9 | TIM      |
| 10 | VODAFONE |
+----+----------+    

mysql> select * from routing_domain;
+----+--------+-----------+-------------+
| id | prefix | domain_id | provider_id |
+----+--------+-----------+-------------+
|  3 | 3932   |         4 |           9 |
|  1 | 39320  |         2 |           9 |
|  2 | 39321  |         3 |          10 |
+----+--------+-----------+-------------+

现在,

给定一个 phone_number '39320xxxxxxx' 和 provider_id 9,我需要得到 domain_id=2; 给定一个 phone_number '39321xxxxxxx' 和 provider_id 9,我需要得到 domain_id=4;

因此,给定某个 phone_number '3932xxxxxxxx' 且 provider_id=9,我需要进行一些最佳匹配搜索。开始搜索 6 个字符的前缀,如果不匹配,尝试使用 5 个字符,依此类推,直到 3 个字符(393)。

我设法从仅从前缀到 5 个字符的 phone_number 搜索中获得了正确的域。

类似:

select * FROM records r
left join routing_domain rd on rd.prefix like SUBSTRING(r.phone_number,1,5) and r.provider_id = rd.provider_id 
left join providers p on p.id = rd.provider_id
left join domains d on d.id = rd.domain_id 
where r.name = 'xxxxxxxxxxxx';

有什么建议可以做这个最佳匹配吗? 非常感谢!

更新

我试过这个:

select * FROM records r
left join routing_domain rd on on r.phone_number like concat(rd.prefix, '%') and r.provider_id = rd.provider_id 
left join providers p on p.id = rd.provider_id
left join domains d on d.id = rd.domain_id 
where r.name = 'xxxxxxxxxxxx';

现在,如果我搜索“39325xxxxxxx”,则会找到前缀为“3932”的匹配项, 但如果我搜索“39320xxxxxxx”,两个前缀都将匹配,搜索返回 2 行。

【问题讨论】:

最大前缀长度为 5 位? 最大前缀长度是 6char(包括国际前缀,所以 '39xxxx'),最小是 3 (39x) 您的要求不完整。但你可以从sqlfiddle.com/#!9/2e36df/7之类的东西开始 缺少什么要求? 你有 2 条记录:routing_domain 表中的 3932、39320 具有相同的 provider_id - 当编号为 393xxxxx.. 时应该返回哪一条?为什么? 【参考方案1】:

一种选择是有一个子查询,它为您提供匹配provider_idprefix 的最长前缀。像这样的:

select domain_id from routing_domain
where
  provider_id = 9
  and '39321xxxxxxx' like concat(prefix, '%')
  and length(prefix) =
  (    select max(length(prefix))
       from routing_domain
       where 
         provider_id = 9
         and '39321xxxxxxx' like concat(prefix, '%')
  )

看我的小提琴here。

【讨论】:

看来可行!我会做更多的测试,以确保它适用于“无电话号码”记录。非常感谢!【参考方案2】:

http://sqlfiddle.com/#!9/2e36df/10

SELECT r.*, 
       MAX(IF(rd.prefix = LEFT(r.phone_number,5),rd.prefix,
             IF(rd.prefix = LEFT(r.phone_number,4),rd.prefix,
                IF(rd.prefix = LEFT(r.phone_number,3),rd.prefix,''))))
FROM records r
LEFT JOIN routing_domain rd
ON r.provider_id = rd.provider_id
GROUP BY r.id

为了更接近你的尝试:

http://sqlfiddle.com/#!9/2e36df/17

SELECT t.*, p.*, d.*
FROM (
  SELECT r.*, 
         MAX(IF(rd.prefix = LEFT(r.phone_number,5),rd.id,
               IF(rd.prefix = LEFT(r.phone_number,4),rd.id,
                  IF(rd.prefix = LEFT(r.phone_number,3),rd.id,'')))) as rd_id
  FROM records r
  LEFT JOIN routing_domain rd
  ON r.provider_id = rd.provider_id
  #WHERE r.phone_number = '393xxxxxxxxxx'
  GROUP BY r.id
  ) t
LEFT JOIN routing_domain rd
ON t.rd_id = rd.id
LEFT JOIN providers p 
ON p.id = rd.provider_id
LEFT JOIN domains d 
ON d.id = rd.domain_id 

【讨论】:

让我们等待你的下一个问题有一天出现...... ;-) 祝你的项目好运

以上是关于前缀最佳匹配 MySQL的主要内容,如果未能解决你的问题,请参考以下文章

MySQL匹配连接表中的最长前缀

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

第四周IP通信

深入浅析Mysql联合索引最左匹配原则

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

MySQL索引