ITPub博客

首页 > Linux操作系统 > Linux操作系统 > 连续相同号码的识别

连续相同号码的识别

原创 Linux操作系统 作者:regonly1 时间:2011-02-25 08:51:08 0 删除 编辑
这个需求看上去比较简单,但是要完成却不是那么轻松的可以做到。
首先看一个例子,假如有若干个手机号码:
13677884444
13988400965
13973250074
13974416087
13999750304
13967311183
13999806632
要找出其中有连续3-8个数字是相同的号码,该如何找?
期初我以为可以用正则很方便的实现,结果想了半天也想不出简洁的匹配模式。只好另辟蹊径了。我想了下,关键问题是如何构建连续3-8个0-9的数字,只要有了这些数字,接下来我们只要找出手机号码中含有这些数字串的就行了。下文给出了几种构造的方式:
1、数字拼接法。即首先生成若干数字,然后按照一定序号通过字符串拼接函数拼接大于该序号的字符串即可。
2、rpad+笛卡尔积法。先构造0-9共10个数字,长度为8的数字串,然后通过笛卡尔及扩展到60个,再按照组内序号进行字符串截断。
3、直接rpad法。构造0-9共10个数字,每个数字重复6次,共60行数字串。然后以数字为分组取组内序号,用rpad进行构建。
4、半正则法。即对0-9每个数字做{3,8}匹配。

下面是几个算法的具体介绍:
数字拼接法:

with
tmp as(select '139' || trunc(dbms_random.value(100000000, 1000000000)) aa
         from dual connect by rownum <= 10000),
ttt as (select replace(ssum(c1), ',') c1, replace(ssum(c2), ',') c2,
               replace(ssum(c3), ',') c3, replace(ssum(c4), ',') c4,
               replace(ssum(c5), ',') c5, replace(ssum(c6), ',') c6
          from (select a.rn,
                       decode(sign(row_number()over(partition by a.rn order by rownum)-4), -1, a.rn) c1,
                       decode(sign(row_number()over(partition by a.rn order by rownum)-5), -1, a.rn) c2,
                       decode(sign(row_number()over(partition by a.rn order by rownum)-6), -1, a.rn) c3,
                       decode(sign(row_number()over(partition by a.rn order by rownum)-7), -1, a.rn) c4,
                       decode(sign(row_number()over(partition by a.rn order by rownum)-8), -1, a.rn) c5,
                       decode(sign(row_number()over(partition by a.rn order by rownum)-9), -1, a.rn) c6
                  from (select rownum - 1 rn from dual connect by rownum <= 10) a,
                       (select rownum rn from dual connect by rownum <= 8) b)
         group by rn)
select * from tmp t, ttt x
 where instr(t.aa, x.c1) > 0
    or instr(t.aa, x.c2) > 0
    or instr(t.aa, x.c3) > 0
    or instr(t.aa, x.c4) > 0
    or instr(t.aa, x.c5) > 0
    or instr(t.aa, x.c6) > 0
查询结果是对的,但是发现一个问题,就是速度太慢,查出100条,就花了26s。看来要把ttt这个表实体化,这样查询才能快点了。事实证明了我的观点,实体化后100条花了0.25s。

rpad+笛卡尔积法
但是我又有了一个想法,能不能不实体化,也让他速度这么快呢?要实现这个目的,只能修改数字串的构造方式了。我们的目的只有一个,构造3-8个0-9的数字串。按照这样的思路,我想到了lpad函数,先构造10行数字(0-9),然后用lpad进行补差:
SQL> select lpad(rownum-1, 8, rownum-1) from dual connect by rownum <= 10;
 
LPAD(ROWNUM-1,8,ROWNUM-1)
-------------------------
00000000
11111111
22222222
33333333
44444444
55555555
66666666
77777777
88888888
99999999
 
10 rows selected
这样便快速得到了我们要的数字串,然后就是每个数字串扩展(3-8):
SQL> select substr(a.n, 1, r) nn
  2    from (select lpad(rownum-1, 8, rownum-1) n from dual connect by rownum <= 10) a,
  3         (select rownum + 2 r from dual connect by rownum <= 6) b
  4  /
 
NN
--------------------------------
000
0000
00000
000000
0000000
00000000
111
1111
11111
111111
1111111
11111111
222
2222
22222
222222
2222222
22222222
333
3333
33333
333333
3333333
33333333
444
4444
44444
444444
4444444
44444444
555
5555
55555
555555
5555555
55555555
666
6666
66666
666666
6666666
66666666
777
7777
77777
777777
7777777
77777777
888
8888
88888
888888
8888888
88888888
999
9999
99999
999999
9999999
99999999
 
60 rows selected
 ok,大功告成,接下去就是匹配了:
SQL> with
  2  tmp as(select '139' || trunc(dbms_random.value(100000000, 1000000000)) aa
  3           from dual connect by rownum <= 10000),
  4  ttt as (select substr(a.n, 1, r) l
  5    from (select lpad(rownum-1, 8, rownum-1) n from dual connect by rownum <= 10) a,
  6         (select rownum + 2 r from dual connect by rownum <= 6) b)
  7  select p.aa from tmp p, ttt t
  8   where instr(p.aa, t.l) >= 1
  9  /
 
AA
-------------------------------------------
139628435559
139111449653
139927388845
139323055525
139947487666
139777541560
.....
100条在0.4s左右。

直接rpad法
上面用的是笛卡尔积构建法,能不能直接就构建60行数字串呢?语句如下:
with
tmp as(select '139' || trunc(dbms_random.value(100000000, 1000000000)) aa
         from dual connect by rownum <= 10000),
ttt as (select rpad(lv, row_number()over(partition by lv order by lv)+2, lv) l
          from (select ceil(level/6)-1 lv
                  from dual
                connect by level <= 60))
select p.aa from tmp p, ttt t
 where instr(p.aa, t.l) >= 1
方法是先构建0-9的数字,每个数字各6行,共60行数字。然后,row_number按数字分组,取组内序号(1-6),并+2(3-8),然后用rpad补差,时间上花费与前面笛卡尔积构建的差不多,都在0.4s左右。

半正则法
最后,再给出半正则的查询法:
with
tmp as(select '139' || trunc(dbms_random.value(100000000, 1000000000)) aa
         from dual connect by rownum <= 10000),
ttt as (select rownum - 1 r from dual connect by rownum <= 10)
select p.aa from tmp p, ttt t
 where regexp_like(p.aa, t.r||'{3,8}')
原理不多说了,很简单的,你懂的。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/12932950/viewspace-687908/,如需转载,请注明出处,否则将追究法律责任。

上一篇: Oracle keep的用法
请登录后发表评论 登录
全部评论

注册时间:2008-05-10

  • 博文量
    257
  • 访问量
    1048658