陪君醉笑三万场,不诉离伤

两个字符串无序匹配问题

上一篇 / 下一篇  2008-08-22 10:07:26 / 个人分类:函数

查看( 33 ) / 评论( 18 )
表t1中有两个字段NO(NUMBER类型),ST(VARCHAR2类型)
表中有如下值
NO           ST
1        'A1A3BCLKMBNK'
2        'A2A4MBKLDMSK'

注:ST字段中的值长度都是2的整数倍;


有一变量V_E,变量值为'A3MLLKNKDS'

现在将t1表中的ST字符与变量V_E进行比较,比较的方法如下:
V_E变量中每两个字符为一个基本单位,ST字段也是以两个字符为一个基本单位,让V_E与ST字段进行比较,得出ST中与V_E基本单位相同的数量

即:
t1表中的值可以看做
NO           ST

1        'A1  A3  BC  LK  MB  NK'
2        'A2  A4  MB  KL  DM  SK'
.        ......................................
.        ......................................
---------------------------------------------
变量V_E可以看做
V_E  -->  'A3 ML LK NK DS'
---------------------------------------------


比较之后得出:

NO           ST                         相同值     相同数量
1        'A1A3BCLKMBNK'      A3 LK NK        3
2        'A2A4MBKLDMSK'                            0
.        ............................         ...               .
.        ............................         ...               .


暂有如下函数,本人感觉效率稍低
------------------------------------------------------------------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION FUN_MATCH_CNT(P_STR VARCHAR2, P_STR2 VARCHAR2) RETURN VARCHAR2 IS
    V_MOTHER VARCHAR2(200);
    V_MATCH  VARCHAR2(20);
    V_LENGTH NUMBER;
    V_LENM   NUMBER :=2;
    V_NUM    NUMBER := 0;
    V_CNT    NUMBER ;
                V_MATCHSTR VARCHAR2(200):=' ';
                V_MATECHED VARCHAR2(200):=' ';
  BEGIN     
          V_CNT  := 0;
    V_MOTHER := P_STR;
                --V_MATCH := P_STR2;
    --将 V_MOTHER值改变成每两个字符被逗号隔开的值
                V_LENGTH := LENGTH(REPLACE(P_STR,',','')) / 2;
    V_LENM   := LENGTH(P_STR2) / 2;
                IF(INSTR(V_MOTHER,',')= 0 ) THEN      
                        FOR X IN 1 .. (V_LENGTH - 1) LOOP
          V_MOTHER := SUBSTR(V_MOTHER, 1, (V_LENGTH - X) * 2) || ',' ||
                  SUBSTR(V_MOTHER, (V_LENGTH - X) * 2 + 1);
      END LOOP;
                END IF;
    --DBMS_OUTPUT.PUT_LINE(V_MOTHER);
    --针对每个基本单位进行比较
    FOR Y IN 1 .. V_LENM LOOP      
                   V_MATCH := SUBSTR(P_STR2, Y * 2 - 1, 2);
       SELECT SIGN(INSTR(V_MOTHER,V_MATCH ))
        INTO V_NUM
        FROM DUAL;
                                IF V_NUM = 1 THEN
                                      V_MATECHED := TRIM(V_MATECHED||V_MATCH);
                                      SELECT SUBSTR(V_MOTHER,1,INSTR(V_MOTHER, V_MATCH)-1)||SUBSTR(V_MOTHER,INSTR(V_MOTHER, V_MATCH)+3)
                                                        INTO V_MOTHER
                                                        FROM DUAL;
              V_CNT := V_CNT + V_NUM;
                                                        --V_CNT := V_CNT + FUN_MATCH_CNT(V_MOTHER,V_MATCH);
                                                        V_MATECHED := TRIM(V_MATECHED||NVL(FUN_MATCH_CNT(V_MOTHER,V_MATCH),' '));
                          END IF;
    END LOOP;                
    ---DBMS_OUTPUT.PUT_LINE(V_CNT);
   -- RETURN V_CNT;
         RETURN TRIM(V_MATECHED);
  END;




SELECT NO,ST ,fun_match_cnt(ST,'A3MLLKNKDS') AS 相同值,NVl(LENGTH(fun_match_cnt(ST,'A3MLLKNKDS')),0)/2 as 相同数量 FROM T1;
-------------------------------------------------------------------------------------------------------------------
NO           ST                         相同值     相同数量
1        'A1A3BCLKMBNK'      A3 LK NK        3
2        'A2A4MBKLDMSK'                            0
.        ............................         ...               .
.        ............................         ...               .


使用递归或者循环匹配方式效率太低,希望各路高人能有更好的方法

感谢各位的踊跃参与,个人不关心是否用到函数或者过程,关键是想提高效率,找高效的方法

[本帖最后由 wghxwl12 于 2008-3-18 14:50 编辑]

TAG: 字符串 匹配

三思笔记,ORACLE学习轨迹~~~~ junsansi 发布于2008-03-13 13:39:41
有点儿意思,用pl/sql实现的话很容易,如果是单条sql的话....................

先标个签,容俺随后想想~~
三思笔记,ORACLE学习轨迹~~~~ junsansi 发布于2008-03-13 14:10:16
[php]
SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bi
PL/SQL Release 10.2.0.3.0 - Production
CORE        10.2.0.3.0        Production
TNS for Linux: Version 10.2.0.3.0 - Production
NLSRTL Version 10.2.0.3.0 - Production

SQL> select * from jss_tmp2;

        ID ST
---------- ------------------------------
         1 A1A3BCLKMBNK
         2 A2A4MBKLDMSK
         3 A3A6LKKLDMSK
         4 DS6LKKLDMSK8
         5 NKA6LKLBDSMK

SQL>
SQL> select e.*,d.value,nvl(d.ct,0) from(
  2  select /*+ rule*/ c.id, c.st, max(substr(sys_connect_by_path(c.str, '-'), 2)) value, c.ct
  3    from (select a.*,
  4                 substr(a.st, b.rn, 2) str,
  5                 count(id) over(partition by id) ct,
  6                 row_number() over(partition by id order by id) rn
  7            from jss_tmp2 a,
  8                 (select rownum * 2 - 1 rn
  9                    from dual
10                  connect by rownum <=
11                             (select max(length(st) / 2) from jss_tmp2)) b
12           where substr(a.st, b.rn, 2) in
13                 (select substr('A3MLLKNKDS', rownum * 2 - 1, 2)
14                    from dual
15                  connect by rownum <= length('A3MLLKNKDS') / 2)) c
16   start with rn = 1
17  connect by prior rn + 1 = rn
18         and prior id = id
19   group by c.id, c.st, c.ct)d, jss_tmp2 e
20   where d.id(+)=e.id
21  /

        ID ST                             VALUE                                                                            NVL(D.CT,0)
---------- ------------------------------ -------------------------------------------------------------------------------- -----------
         1 A1A3BCLKMBNK                   A3-NK-LK                                                                                   3
         2 A2A4MBKLDMSK                                                                                                              0
         3 A3A6LKKLDMSK                   LK-A3                                                                                      2
         4 DS6LKKLDMSK8                   DS                                                                                         1
         5 NKA6LKLBDSMK                   NK-DS-LK                                                                                   3

SQL>
--
[/php]
zhanglinjun007发布于2008-03-13 15:12:14

QUOTE:

原帖由 junsansi 于 2008-3-13 14:10 发表
[php]
SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bi
PL/SQL Release 10.2.0.3.0 - Production
CORE        10.2.0.3.0        Production
TNS for Linux: Version 10.2.0.3.0 - Production
NLSRTL Version 10.2.0.3.0 - Production

SQL> select * from jss_tmp2;

        ID ST
---------- ------------------------------
         1 A1A3BCLKMBNK
         2 A2A4MBKLDMSK
         3 A3A6LKKLDMSK
         4 DS6LKKLDMSK8
         5 NKA6LKLBDSMK

SQL>
SQL> select e.*,d.value,nvl(d.ct,0) from(
  2  select /*+ rule*/ c.id, c.st, max(substr(sys_connect_by_path(c.str, '-'), 2)) value, c.ct
  3    from (select a.*,
  4                 substr(a.st, b.rn, 2) str,
  5                 count(id) over(partition by id) ct,
  6                 row_number() over(partition by id order by id) rn
  7            from jss_tmp2 a,
  8                 (select rownum * 2 - 1 rn
  9                    from dual
10                  connect by rownum  
高!!!  学习了 !!
zhanglinjun007发布于2008-03-13 15:15:45
君三思的要在10G里运行,如果要在9I中运行,把其中的代码改为:
(select level * 2 - 1 rn
                      from (select max(length(st) / 2)  len from jss_tmp2)
                   connect by level <=len)
bell6248的个人空间 bell6248 发布于2008-03-13 15:20:00


这个要求用sql语句不如用pl/sql!


SQL> select * from tmp22;

        NO ST
---------- ------------------------------
         1 A1A3BCLKMBNK
         2 A2A4MBKLDMSK

SQL>
SQL> select        no,
  2                max_str st,
  3                max(st) "相同值",
  4                nvl(cnt, 0) "相同数量"
  5  from
  6  (select       no,
  7                replace(max_str, ',', '') max_str,
  8                LTRIM(SYS_CONNECT_BY_PATH(str, ' '), ' ') st,
  9                cnt
10  from
11  (select      no,
12               max_str,
13               str,
14               row_number() over(partition by no order by cnt) rn,
15               cnt
16  from
17  (select     tmp1.no,
18              tmp1.max_str,
19              tmp2.str,
20              decode(tmp2.str, null, null, count(tmp2.str) over(partition by no)) cnt
21  from
22  (select    no,
23             st,
24             max(st) over(partition by no) max_str,
25             str,
26             cnt
27  from
28  (select   no,
29            LTRIM(SYS_CONNECT_BY_PATH(str, ','), ',') st,
30            str,
31            cnt
32  from
33  (select  no,
34           st,
35           str,
36           count(*) over(partition by no) cnt,
37           row_number() over(partition by no order by no) rn
38  from
39  (select a.no,
40          a.st,
41          b.rn,
42          substr(a.st, b.rn, 2) str
43    from tmp22 a,
44         (select level * 2 - 1 rn
45            from (select max(length(st)) len from tmp22)
46          connect by level <= len) b
47  order by a.no, b.rn)
48  where str is not null)
49  start with rn = 1
50  connect by prior rn + 1 = rn and prior no = no)) tmp1,
51  (select substr('A3MLLKNKDS', rownum * 2 - 1, 2) str
52     from dual
53  connect by rownum <= length('A3MLLKNKDS') / 2) tmp2
54  where tmp1.str = tmp2.str(+)))
55  start with rn = 1
56  connect by prior rn + 1 = rn and prior cnt = cnt and prior no = no)
57  group by  no, max_str, cnt;

        NO ST                   相同值                   相同数量
---------- -------------------- ------------------------ ----------------------------------------
         1 A1A3BCLKMBNK         A3 LK NK                 3
         2 A2A4MBKLDMSK                                  0

SQL>



[ 本帖最后由 bell6248 于 2008-3-13 15:21 编辑 ]
三思笔记,ORACLE学习轨迹~~~~ junsansi 发布于2008-03-13 15:32:09

QUOTE:

原帖由 bell6248 于 2008-3-13 15:20 发表


这个要求用sql语句不如用pl/sql!


SQL> select * from tmp22;

        NO ST
---------- ------------------------------
         1 A1A3BCLKMBNK
         2 A2A4MBKLDMSK

SQL>
SQL> select        no,
  2                max_str st,
  3                max(st) "相同值",
  4                nvl(cnt, 0) "相同数量"
  5  from
  6  (select       no,
  7                replace(max_str, ',', '') max_str,
  8                LTRIM(SYS_CONNECT_BY_PATH(str, ' '), ' ') st,
  9                cnt
10  from
11  (select      no,
12               max_str,
13               str,
14               row_number() over(partition by no order by cnt) rn,
15               cnt
16  from
17  (select     tmp1.no,
18              tmp1.max_str,
19              tmp2.str,
20              decode(tmp2.str, null, null, count(tmp2.str) over(partition by no)) cnt
21  from
22  (select    no,
23             st,
24             max(st) over(partition by no) max_str,
25             str,
26             cnt
27  from
28  (select   no,
29            LTRIM(SYS_CONNECT_BY_PATH(str, ','), ',') st,
30            str,
31            cnt
32  from
33  (select  no,
34           st,
35           str,
36           count(*) over(partition by no) cnt,
37           row_number() over(partition by no order by no) rn
38  from
39  (select a.no,
40          a.st,
41          b.rn,
42          substr(a.st, b.rn, 2) str
43    from tmp22 a,
44         (select level * 2 - 1 rn
45            from (select max(length(st)) len from tmp22)
46          connect by level  

adaiagua发布于2008-03-13 15:34:00
主要是合并匹配字符串那部分费事,用SYS_CONNECT_BY_PATH的话,SQL必然冗长。。。
如果能用自定义聚合函数实现的话,sql就简单了很多
select A.NO, MIN(A.ST), strcat(C.SUBST2), COUNT(*)
from A
  ,(
    select substr('A3MLLKNKDS', level * 2 - 1, 2) as subST2
    from dual
    connect by level <= length('A3MLLKNKDS') / 2
   ) C
where mod(instr(A.ST, C.SUBST2(+)), 2) = 1
group by A.NO

strcat的定义参考
http://www.cnblogs.com/sunsonbaby/archive/2005/01/21/95435.html

[ 本帖最后由 adaiagua 于 2008-3-13 15:38 编辑 ]
bell6248的个人空间 bell6248 发布于2008-03-13 15:43:08

QUOTE:

原帖由 adaiagua 于 2008-3-13 15:34 发表
主要是合并匹配字符串那部分费事,用SYS_CONNECT_BY_PATH的话,SQL必然冗长。。。
如果能用自定义聚合函数实现的话,sql就简单了很多
select A.NO, MIN(A.ST), strcat(C.SUBST2), COUNT(*)
from A
  ,(
    select substr('A3MLLKNKDS', level * 2 - 1, 2) as subST2
    from dual
    connect by level  
好啊, 又有人提供的一种方法!
三思笔记,ORACLE学习轨迹~~~~ junsansi 发布于2008-03-13 15:48:25
如果用函数实现行列转换,操作方式就更灵活的,野花的blog里我记的专门有篇文章总结这个~~

不过我觉着事有两面性,毕竟对于行列转换并非常见应用,多数情况下都是特殊需求,单纯为某个特殊需求弄个函数呵呵,我觉着dba也不一定乐意操作啊~~~

而且像论坛这种回答问题的形式,人家问行列转换你贴个函数上去,人家也未必会采用,所以一条sql实现吧,只当熟练语法了
adaiagua发布于2008-03-13 16:03:46
仁者见仁,智者见智,

权当凑个热闹吧。。。
louis_xu的个人空间 louis_xu 发布于2008-03-13 16:07:25
三思哥哥就是牛啊
louis_xu的个人空间 louis_xu 发布于2008-03-13 17:19:43
select rownum * 2 - 1 rn
     from dual
connect by rownum <= (select max(length(st) / 2)
                                         from jss_tmp2)

我执行这段报错:ORA-01473: cannot have subqueries in CONNECT BY clause

WHY?
louis5421发布于2008-03-13 18:33:14
不能嵌套查询 connect by
louis5421发布于2008-03-13 18:35:57

QUOTE:

原帖由 adaiagua 于 2008-3-13 15:34 发表
主要是合并匹配字符串那部分费事,用SYS_CONNECT_BY_PATH的话,SQL必然冗长。。。
如果能用自定义聚合函数实现的话,sql就简单了很多
select A.NO, MIN(A.ST), strcat(C.SUBST2), COUNT(*)
from A
  ,(
    select substr('A3MLLKNKDS', level * 2 - 1, 2) as subST2
    from dual
    connect by level  
支持兄弟,这样多清楚啊,一个简单的功能非得用sql来实现,看着复杂而且不利于维护!
DragonBill的个人空间 DragonBill 发布于2008-03-14 09:52:22
mark
DragonBill的个人空间 DragonBill 发布于2008-03-14 12:35:45

QUOTE:

原帖由 junsansi 于 2008-3-13 15:48 发表
如果用函数实现行列转换,操作方式就更灵活的,野花的blog里我记的专门有篇文章总结这个~~

不过我觉着事有两面性,毕竟对于行列转换并非常见应用,多数情况下都是特殊需求,单纯为某个特殊需求弄个函数呵呵,我觉着dba也不一定乐意操作啊~~~

而且像论坛这种回答问题的形式,人家问行列转换你贴个函数上去,人家也未必会采用,所以一条sql实现吧,只当熟练语法了

对, 我觉得像这种问题: 能用一条SQL完成的, 就用一条SQL,
权当练习对SQL的熟练程度
DragonBill的个人空间 DragonBill 发布于2008-03-14 12:36:10
WITH A AS
(
SELECT 'A1A3BCLKMBNK' AS STR FROM DUAL
UNION
SELECT 'A2A4A3KLDMNK' AS STR FROM DUAL
)
SELECT STR, SUB_STR_LEN / 2 AS "Frequence", SUB_STR
FROM
(
  SELECT STR, LTRIM(SYS_CONNECT_BY_PATH(SUB_STR, ' '), ' ') AS SUB_STR,
         FLAG, SUB_STR_LEN
  FROM
  (
    SELECT ROW_NUMBER() OVER (PARTITION BY STR ORDER BY STR) AS SEQ, STR,
           (LENGTHB(STR) - LENGTHB(REPLACE(STR,SUB_STR, '')))/2 AS FLAG,
           SUB_STR,
           COUNT(STR) OVER (PARTITION BY STR) * 2 AS SUB_STR_LEN
    FROM  A,
         (SELECT SUBSTR('A3MLLKNKDS', (LEVEL-1)*2+1, 2) SUB_STR
          FROM DUAL
          CONNECT BY LEVEL <= LENGTHB('A3MLLKNKDS')/2
          ) B   
    WHERE (LENGTHB(STR) - LENGTHB(REPLACE(STR,SUB_STR, '')))/2 > 0
  )
  CONNECT BY PRIOR SEQ + 1 = SEQ AND PRIOR STR = STR
)
WHERE LENGTH(REPLACE(SUB_STR, ' ', '')) = SUB_STR_LEN;
陌生的彼岸 wghxwl12 发布于2008-03-18 14:51:31
感谢大家踊跃回帖

我来说两句

(可选)

日历

« 2008-12-03  
 123456
78910111213
14151617181920
21222324252627
28293031   

数据统计

  • 访问量: 1500
  • 日志数: 1399
  • 建立时间: 2007-12-14
  • 更新时间: 2008-11-20

RSS订阅

Open Toolbar