MySQL最佳实践:匹配前缀

vbe*_*nce 5 mysql

我有一个带代码的表和另一个带前缀的表.我需要匹配每个代码的(最长)前缀.

还有一个辅助范围,我必须限制前缀(这涉及引入其他表).我不认为这在大多数情况下都很重要,但这里是一个简化的(规范化的)方案(我必须设置item.prefix_id):

group (id)
subgroup (id, group_id)
prefix (id, subgroup_id, prefix)
item (id, group_id, code, prefix_id)
Run Code Online (Sandbox Code Playgroud)

可以在新字段中缓存前缀的长度并对其进行索引.将group_id缓存在前缀表中是可以的(尽管组是相当小的表,在大多数情况下我认为不会获得任何性能提升).item表包含几十万条记录,前缀最多包含500条.

编辑:

对不起如果问题定义不够.当使用单词"prefix"时,我实际上是指它,所以代码必须以实际前缀开头.

subgroup
id   group_id
-------------
1    1
2    1
3    1
4    2

prefix
id   subgroup_id  prefix
------------------------
1    1            a
2    2            abc
3    2            123
4    4            abcdef

item
id   group_id     code    prefix_id
-----------------------------------
1    1            abc123  NULL
2    1            abcdef  NULL
3    1            a123    NULL
4    2            abc123  NULL
Run Code Online (Sandbox Code Playgroud)

前缀列的预期结果是(item.id,item.prefix_id):

(1,2)因为:子组1,2,3在组1下,代码abc123以前缀a和前缀开头,abc并且abc是两者的最大值,所以我们取其id abc为2并将其放入item.prefix_id.

(2,2)因为:即使前缀{4}(是abcdef)是最喜欢的匹配前缀,它的子组(也就是4)在组2下,但是项目在组1下,所以我们可以从子组1中选择, 2,3,仍然abc是三个可能的前缀中的最佳匹配.

(3,1)因为:a是最热门的比赛.

(4,NULL)因为:第4项在第2组下,并且第2组下的唯一前缀与之abcdef不匹配abc123(因为abc123不以第一组开头abcdef).

但正如我所说,整个摸索的事情并不是问题的重要组成部分.我主要关注的是将一个带有可能前缀的表与一个字符串表进行匹配,以及如何以最佳方式进行匹配.(最好意味着可读性,可维护性和性能之间的最佳权衡 - 因此标题中的"最佳预告").

目前我做的事情如下:

UPDATE item USE INDEX (code3)
    LEFT JOIN prefix ON prefix.length=3 AND LEFT(item.code,3)=prefix.prefix
    LEFT JOIN subgroup ON subgroup.id=prefix.subgroup_id
WHERE subgroup.group_id == item.group_id AND
    item.segment_id IS NULL
Run Code Online (Sandbox Code Playgroud)

哪里code3KEY code3 (segment_id, group_id, code(3)). - 同样的逻辑以1,2,3和4作为长度重复.它似乎非常有效,但我不喜欢它中存在重复(单个操作的4个查询). - 当然这是前缀的最大长度为4的情况.

感谢大家分享您的想法.

Kar*_*lis 2

将group_id缓存在前缀表中就可以了。

因此,让我们group_id在表前缀中创建列并用适当的值填充该列。我假设您知道如何执行此操作,所以让我们进入下一步。

我们将从这个综合索引中获得的最大性能优势:

ALTER TABLE `prefix` ADD INDEX `c_index` (
    `group_id` ASC, 
    `prefix` ASC
);
Run Code Online (Sandbox Code Playgroud)

以及更新语句:

UPDATE item i
SET 
    prefix_id = (
        SELECT p.id
        FROM prefix p USE INDEX (`c_index`)
        WHERE 
            p.group_id = i.group_id AND 
            p.prefix IN (
                LEFT(i.code, 4), 
                LEFT(i.code, 3), 
                LEFT(i.code, 2), 
                LEFT(i.code, 1)
            )                
        ORDER BY LENGTH(p.prefix) DESC
        LIMIT 1        
    )
Run Code Online (Sandbox Code Playgroud)

在此示例中,我假设前缀是可变长度 {1,4}。我决定使用IN子句而不是LIKE来充分利用c_index的好处。