MySQL匹配连接表中的最长前缀
Posted
技术标签:
【中文标题】MySQL匹配连接表中的最长前缀【英文标题】:MySQL matching longest prefix in joined tables 【发布时间】:2017-08-27 16:48:55 【问题描述】:我们有 2 个表,“callsdb”保存电话记录,“regionsdb”保存区域相关前缀。 我们需要做的是生成一个匹配“callsdb.destination_number”和“regionsdb.country_prefix”的LONGEST前缀的连接列表。 p>
注意事项:
1) “callsdb”中的电话号码可能有也可能没有有“1001”前缀。
2) “callsdb”中的电话号码在国家/地区前缀之前有一个额外的“00”前缀。
我们尝试了以下查询,但它的性能极差。 有没有更快的方法来实现这一点?
SELECT
uid, call_date, destination, name, country_prefix, duration, unit_cost
FROM
callsdb.calls
INNER JOIN
regionsdb.regions
ON
(
(
(calls.destination_number LIKE CONCAT('100100', regions.country_prefix, '%'))
AND
(
LENGTH(regions.country_prefix) =
(
SELECT MAX(LENGTH(regions.country_prefix))
FROM regionsdb.regions
WHERE calls.destination_number LIKE CONCAT('100100', regions.country_prefix, '%')
)
)
)
OR
(
(calls.destination_number LIKE CONCAT('00', regions.country_prefix, '%'))
AND
(
LENGTH(regions.country_prefix) =
(
SELECT MAX(LENGTH(regions.country_prefix))
FROM regionsdb.regions
WHERE calls.destination_number LIKE CONCAT('00',regions.country_prefix, '%')
)
)
)
)
ORDER BY call_date DESC;
编辑:
通过更改查询的“ON”部分减少了执行时间。但是还是太慢了!
ON
(
(
(IF(calls.destination_number LIKE '1001%', SUBSTRING(calls.destination_number, 5), calls.destination_number) LIKE CONCAT('00', regions.country_prefix, '%'))
AND
(
LENGTH(regions.country_prefix) =
(
SELECT MAX(LENGTH(regions.country_prefix))
FROM regionsdb.regions
WHERE IF(calls.destination_number LIKE '1001%', SUBSTRING(calls.destination_number, 5), calls.destination_number) LIKE CONCAT('00', regions.country_prefix, '%')
)
)
)
)
编辑 2: 在选择“dst”时更正为“destination”。
【问题讨论】:
(1) 从select
子句中不清楚哪个表有哪些字段。你能提供表结构,或者在每个字段前面加上表名吗? (2)calls的主键是什么?
@trincot 字段为:calls.uid、calls.call_date、calls.destination、regions.name、regions.country_prefix、regions.duration、regions.unit_cost 呼叫的主键是 uid。
抱歉,“duration”属于“calls”表。
【参考方案1】:
您可以将group by
与group_concat
结合使用,这允许将regions
表中的字段按照country_prefix
长度的降序连接。然后你可以提取第一个。
我将假设字段name
、unit_cost
来自regions
表,如果其他字段也是如此,则对它们应用相同的逻辑。 group by
子句应列出 calls
表中的所有选定字段,或至少列出关键字段:
select c.uid, c.call_date, c.dst,
substring_index(
group_concat(r.name order by length(r.country_prefix) desc),
',', 1) as name,
substring_index(
group_concat(r.country_prefix order by length(r.country_prefix) desc),
',', 1) as country_prefix,
c.duration,
substring_index(
group_concat(format(r.unit_cost, 2)
order by length(r.country_prefix) desc),
',', 1) as unit_cost
from callsdb.calls c
inner join regionsdb.regions r
on (left(c.destination_number, 6 + length(r.country_prefix))
= concat('100100', r.country_prefix)
or mid(c.destination_number, 3, length(r.country_prefix))
= r.country_prefix
)
group by c.uid,
c.call_date,
c.dst,
c.duration
【讨论】:
这看起来很有希望,谢谢!它的执行时间是我的查询的一半。但是为什么“unit_cost”显示为 0x302E...?? 好的,那是因为unit_cost
没有转换为字符串:我编辑了我的答案:使用format
函数。
现在 unit_cost 显示正常,谢谢。查询需要约 14 秒才能获得约 6000 条记录结果。我想这是我们能得到的最好的表现。
为了进一步改进,我将研究在进入时去除 1001 和 00 的触发器:显然它没有任何作用。如果是这样,那么它可以由该触发器在另一列中写入。
我是否必须按我在查询中使用的“呼叫”表的每个字段进行分组?以上是关于MySQL匹配连接表中的最长前缀的主要内容,如果未能解决你的问题,请参考以下文章
MySQL 最长前缀匹配 100 万条记录与 3000 种可能性