在ORACLE中搜索最长前缀的最快方法

时间:2013-05-27 15:03:48

标签: sql oracle algorithm string-matching

我有一个为大量区域定义的电话号码前缀列表(在由gvcode和cgi定义的查询中)。 我需要有效地找到与给定数字PHONE_NR匹配的最长前缀。

我在字段数字上使用倒置LIKE子句(其中包含格式为+ 48%,+ 49%,+ 1%,+ 1232%等的前缀)。

因此我不能在该字段上使用普通索引。

通过在gvcode和cgi字段上使用IOT(这是主键的一部分(前两个列)),我设法获得了实质性的改进。 我还查看了一些oracle文本索引,但找不到一个匹配表中较短前缀的较长输入。

是否还有其他方法可以执行比此方法更快的搜索。

这是一个查询,它给出了所有匹配前缀的列表(我之后按数字长度对其进行排序)。

  select  t.gvcode,  t.digits
                from NUMBERS t 
                    where 
                        t.gvcode=ZONE_SET_CODE 
                        and t.cgi=cgi_f
                       and ( PHONE_NR like t.digits)
                         order by length(digits) desc 

5 个答案:

答案 0 :(得分:1)

我可能听起来很愚蠢,但当我遇到这样的问题时,我进入了大多数非节省空间的蛮力方式:

让我们说:

L=length of longest prefix to match (without obvious +, of course)

添加L个命名的其他字段,例如P1, P2,...,PL

使用

更新这些字段
UPDATE NUMBERS set P1=SUBSTR(PHONE_NR,1,1), P2=SUBSTR(PHONE_NR,1,2), ..., PL=SUBSTR(PHONE_NR,1,L)

(将来你也可以在INSERT OR UPDATE触发器中执行此操作)

现在你有L个字段来创建索引并与你喜欢的任何东西进行比较。

答案 1 :(得分:1)

除了“数字”索引外,您还可以在rpad(substr(digits,1,length(digits)-1), 10, '9')上创建索引。 “10”是您要支持的最大长度。您将在where子句中添加一个附加条件:rpad(substr(digits,1,length(digits)-1), 10, '9') >= PHONE_NR

您的SQL将是:

select  t.gvcode,  t.digits
from NUMBERS t 
    where 
        t.gvcode=ZONE_SET_CODE 
        and t.cgi=cgi_f
       and PHONE_NR like t.digits
       and substr(digits, 1, length(digits)-1) <= PHONE_NR
       and rpad(substr(digits,1,length(digits)-1), 10, '9') >= PHONE_NR
order by length(digits) desc 

以下是sqlfiddle

中的示例

答案 2 :(得分:1)

我遇到了同样的问题,我发现此解决方案很有用(在https://community.oracle.com/thread/351988上归功于L. Schneider):

create table a (a varchar2(100));
create index a_1 on a(a);

begin
 delete a;
 insert into a values ('00431');
 insert into a values ('004312');
 insert into a values ('0043123');
 insert into a values ('00431234');
 insert into a values ('004312345');
end;
/


select max(a)
  from a 
 where '004311' like a||'%'
;

http://sqlfiddle.com/#!4/abc975/1

答案 3 :(得分:0)

我不确定这会有什么帮助,但我认为值得一试。

substr(digits, 1, length(digits)-1)上创建基于函数的索引(这仅用于索引没有'%'的数字)

然后在您的查询中,您可以添加另一个条件:

AND substr(digits, 1, length(digits)-1) <= PHONE_NR

Here is a sqlfiddle demo

我们的想法是,通过 lexical 比较,您可以“删除”所有数字,这些数字都在PHONE_NR之后

答案 4 :(得分:0)

好的,只是写作,因为我有同样的问题。 如果您知道前缀长度的范围,则可以执行与以下内容类似的操作。以下示例假定前缀长度为2-6

select  t.num,  coalesce(p6.PREFIX, p5.PREFIX, p4.PREFIX, p3.PREFIX, p2.PREFIX) PREFIX
  from NUMBERS t
LEFT OUTER JOIN PREFIXES p2 ON substr(t.num,1,2)=p2.PREFIX  
LEFT OUTER JOIN PREFIXES p3 ON substr(t.num,1,3)=p3.PREFIX  
LEFT OUTER JOIN PREFIXES p4 ON substr(t.num,1,4)=p4.PREFIX  
LEFT OUTER JOIN PREFIXES p5 ON substr(t.num,1,5)=p5.PREFIX  
LEFT OUTER JOIN PREFIXES p6 ON substr(t.num,1,6)=p6.PREFIX  

平等连接就像你能得到的一样好。

我认为它比其他任何可能的解决方案都要好得多,希望它可以帮助任何偶然发现同一问题的人

Sqlfiddle link修改了sailaway的答案,其脚本仍然提供所有匹配而不是最长的匹配