2011-07-05 83 views
5

我有码表和其它表前缀。我需要匹配每个代码的(最长)前缀。MySQL的最佳实践:匹配前缀

还有其中我不得不限制前缀二次范围(这包括使其它表中)。我不认为这将此事在大多数情况下,但这里是一个简化(标准化)方案(我必须设置item.prefix_id):

group (id) 
subgroup (id, group_id) 
prefix (id, subgroup_id, prefix) 
item (id, group_id, code, prefix_id) 

它好吧缓存前缀的长度在一个新的领域和索引它。在前缀表中缓存group_id是好事(尽管组是相当小的表,在大多数情况下,我认为不会有性能提升)。 项目表包含几十万条记录,包含前缀最多500

编辑:

很抱歉,如果没有足够的定义的问题。当使用单词“前缀”时,我的意思是,所以代码必须以实际前缀开始

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 

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

(1,2)由于:subroups 1,2,3是下组1,将码abc123开始与前缀a和前缀abcabc是LOGEST两个的,所以我们采取的abc这是2的ID,并把它变成item.prefix_id。因为:即使前缀{4}(即abcdef)是最匹配的前缀,它的子组(即4)在组2中,但该项在组1中,所以我们可以从子组1,2,3中选择,仍然是abc是三个可能的前缀中的最低匹配。

(3,1)由于:a是LOGEST匹配。

(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 

code3KEY code3 (segment_id, group_id, code(3))。 - 同样的逻辑重复1,2,3和4作为长度。这看起来很有效,但我不喜欢它中存在重复(4个查询单个操作)。 - 当然这是在当前缀的最大legth是4

感谢大家分享你的想法这么远。

+0

到目前为止您尝试了哪些查询? –

+0

你用两个相同长度的前缀做什么? –

+0

@vbence'code'列的类型是什么?如果varchar,那么varchar的长度是多少?和前缀相同的问题。 – Karolis

回答

2

在前缀表中缓存group_id是好事。

所以,让我们在表前缀创建列group_id并用适当的值填充列。我假设你知道如何做到这一点,所以让我们继续下一步。

最大的性能优势,我们将从这个复合指数得到:

ALTER TABLE `prefix` ADD INDEX `c_index` (
    `group_id` ASC, 
    `prefix` ASC 
); 

而且UPDATE声明:

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   
    ) 

在这个例子中,我假设前缀是可变长度{ 1,4}。我一起决定使用IN子句,而不是LIKE,以获得c_index的全部好处。

+0

我添加了一些示例数据来澄清问题。 – vbence

+0

我认为你非常接近他的更新查询所需的vbence。一个问题,但。您的查询只是通过组ID识别,而不考虑来自“Prefix.Prefix = Item”的匹配文本。代码“(例如:商品代码必须以与Prefix.prefix相同的值开始,它与...相连)修复这个问题,我认为你需要什么 – DRapp

+0

@DRapp是的,但它并不那么简单,因为修改不会让我们使用** ORDER BY **的索引 – Karolis

1

除非我过于简化,应尽可能简单...(不管是否有多个每码相同的长度)开始的内部预查询来获得最长前缀

select 
     PreQuery.Code, 
     P2.ID, 
     P2.SubGroup_ID, 
     P2.Prefix 
    From 
     (select 
       i.code, 
       max(length(trim(p.Prefix))) as LongestPrefix 
      from 
       item i 
       join prefix p 
        on i.prefix_id = p.id 
      group by 
       i.code) PreQuery 
     Join item i2 
     on PreQuery.Code = i2.Code 
     Join Prefix P2 
      on i2.Prefix_ID = P2.ID 
      AND PreQuery.LongestPrefix = length(trim(P2.Prefix))) 

现在,如果你想对那些有多个具有相同前缀长度的地方做一些特殊的事情,它将需要一些调整,但这应该为你做。

+0

'item.prefix_id'具有NULL值,任务是设置它的值。 – vbence

+0

@vbence,那么你可以提供每一个相应的表的几行来显示你有什么? – DRapp

+0

除了item.prefix_id,每个字段都有正确的值 - 正如我在原始帖子中所写的:“我必须设置item.prefix_id”。我会在早上提供更多信息。 – vbence

1

要重新回答,因为您尝试更新元素,请尝试以下更新查询。现在,这里是围绕这个......“PreQuery”实际上会返回给定项目的所有匹配前缀......但是,由于顺序基于前缀长度,对于那些具有多个匹配“前缀”的条目, ,它将首先用最短的前缀更新,然后用下一个较长的前缀打记录,最后以最长的匹配结束。所以最后,它应该得到你所需要的。

话虽这么说(我不能现在具体测试),如果只是更新基于针对给定的ID中的第一项,然后就做出降前缀长度的订单的订单。

update Item, 
      (SELECT 
        I.ID, 
        P.ID Prefix_ID, 
        P.Prefix, 
        I.Code, 
        LENGTH(TRIM(P.Prefix)) as PrefixLen 
       FROM 
        Item I 
         JOIN SubGroup SG 
         ON I.Group_ID = SG.Group_ID 
          JOIN Prefix P 
           ON SG.ID = P.SubGroup_ID 
           AND LEFT(P.Prefix, LENGTH(TRIM(P.Prefix))) 
           = LEFT(I.Code, LENGTH(TRIM(P.Prefix))) 
       ORDER BY 
        I.ID, 
        LENGTH(TRIM(P.Prefix)) ) PreQuery 
     set 
     Prefix_ID = PreQuery.Prefix_ID 
     where 
     ID = PreQuery.ID 
+0

@vbence,你有没有机会尝试这个解决方案... – DRapp