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

Posted

技术标签:

【中文标题】在 ORACLE 中搜索最长前缀的最快方法【英文标题】:Fastest way to search for longest prefix in ORACLE 【发布时间】:2013-05-22 11:18:18 【问题描述】:

我有一个为大量区域定义的电话号码前缀列表(在由 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 

【问题讨论】:

也许如果您在 substr(digits, 2, length(digits)-1) 上创建一个基于函数的索引,然后将另一个条件 and substr(digits, 2, length(digits)-1) <= PHONE_NR 添加到查询中,您可以在某些情况下看到一些改进 应该是and substr(digits, 1, length(digits)-1) <= PHONE_NR(不需要去掉'+') 【参考方案1】:

我不确定这是否真的有帮助,但我认为值得一试。

substr(digits, 1, length(digits)-1) 上创建一个基于函数的索引(这只是为了索引没有“%”的数字)

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

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

Here is a sqlfiddle demo

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

【讨论】:

【参考方案2】:

我可能听起来很愚蠢,但是当我遇到这样的问题时,我采用了最不节省空间的蛮力方式:

让我们说:

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 个字段可以创建索引并与您喜欢的任何内容进行比较。

【讨论】:

【参考方案3】:

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

【讨论】:

【参考方案4】:

好的,写信是因为我有同样的问题。 如果您知道您拥有的前缀长度的范围,您可以执行类似于以下的操作。以下示例假定前缀长度为 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 的答案,其脚本仍然提供所有匹配项,而不仅仅是最长的匹配项

【讨论】:

【参考方案5】:

我遇到了同样的问题,我发现这个解决方案很有用(感谢 L. Schneider https://community.oracle.com/thread/351988):

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

【讨论】:

以上是关于在 ORACLE 中搜索最长前缀的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

渐进式搜索最长前缀

用于搜索最长公共前缀的 SQL 查询

在向量中找到最长的“连续数字”条纹的最快方法是啥?

题解 P1470 最长前缀 Longest Prefix

找到位数组的最长前缀

有没有更好的方法(最快)来获得最长的公共文件夹路径?