如何在Oracle varchar值中计算一个字符出现的次数?

53

我应该如何统计一个varchar2字符串中字符 - 出现的次数?

示例:

select XXX('123-345-566', '-') from dual;
----------------------------------------
2

1
关于性能,我们有趣地发现REGEXP_COUNT解决方案比LENGTH-LENGTH(REPLACE())方法慢了约5倍(更消耗CPU时间)。Oracle 11.2.0.4 Linux x86 64位。 - Rainer Stenzel
9个回答

87

给你:

select length('123-345-566') - length(replace('123-345-566','-',null)) 
from dual;

严格来说,如果您想检查的字符串只包含要计数的字符,则上述查询将返回NULL;以下查询将在所有情况下都给出正确答案:

select coalesce(length('123-345-566') - length(replace('123-345-566','-',null)), length('123-345-566'), 0) 
from dual;
< p>在 coalesce 中的最后一个0捕获了在空字符串(即NULL,因为在ORACLE中,length( NULL)= NULL)中计数的情况。


该代码中的coalesce函数用于返回参数列表中第一个非空表达式的值。如果所有参数都是空,则返回null。该句子中的最后一个0指的是当所有参数都为空时,coalesce函数将返回0。而原文中提到的0是特指在空字符串中进行计数时的情况,这里应该是笔误。

2
非常聪明的回答 ;) - kevthanewversi
我认为这可能取决于MySQL版本和/或设置,但在那里使用“null”可能会导致空结果。最好为replace()调用使用空字符串:length(replace('123-345-566','-','')) - John Rix

80

REGEXP_COUNT应该能够解决问题:

select REGEXP_COUNT('123-345-566', '-') from dual;

5
只支持Oracle 11。不过这是一个不错的解决方案。 - Flukey
+1 很好知道也有 REGEXP_COUNT 函数。 - bpgergo
遗憾,没有注意到 OP 在使用 10g 版本。 - Borodin

13

这里有一个想法:尝试用空字符串替换除破折号字符外的所有内容。然后计算剩下了多少个破折号。

select length(regexp_replace('123-345-566', '[^-]', '')) from dual

5

我刚遇到了非常类似的问题... 但是 RegExp_Count 无法解决它。 字符串 '16,124,3,3,1,0,' 中包含多少次 ',3,'?我们看到有2次,但 RegExp_Count 只返回1次。同样的情况也出现在 'bbaaaacc' 字符串中,在其中查找 'aa' - 应该有3次,但 RegExp_Count 只返回2次。

select REGEXP_COUNT('336,14,3,3,11,0,' , ',3,') from dual;
select REGEXP_COUNT('bbaaaacc' , 'aa') from dual;

我花了一些时间在网上研究解决方案。没有找到......所以我编写了自己的函数,返回出现次数为TRUE的数字。希望它能有用。

CREATE OR REPLACE FUNCTION EXPRESSION_COUNT( pEXPRESSION VARCHAR2, pPHRASE VARCHAR2 ) RETURN NUMBER AS
  vRET NUMBER := 0;
  vPHRASE_LENGTH NUMBER := 0;
  vCOUNTER NUMBER := 0;
  vEXPRESSION VARCHAR2(4000);
  vTEMP VARCHAR2(4000);
BEGIN
  vEXPRESSION := pEXPRESSION;
  vPHRASE_LENGTH := LENGTH( pPHRASE );
  LOOP
    vCOUNTER := vCOUNTER + 1;
    vTEMP := SUBSTR( vEXPRESSION, 1, vPHRASE_LENGTH);
    IF (vTEMP = pPHRASE) THEN        
        vRET := vRET + 1;
    END IF;
    vEXPRESSION := SUBSTR( vEXPRESSION, 2, LENGTH( vEXPRESSION ) - 1);
  EXIT WHEN ( LENGTH( vEXPRESSION ) = 0 ) OR (vEXPRESSION IS NULL);
  END LOOP;
  RETURN vRET;
END;

2
正则表达式通过遍历字符串来工作,而不是每次从开头重新开始搜索,因此REGEXP_COUNT()将始终并且正确地(从正则表达式的角度)返回您的示例以及类似的示例中的1。它找到第一次出现的,3,,然后从字符串中的下一个位置开始寻找模式,但没有找到。 - David Faber

3

我想到了

 SELECT LENGTH('123-345-566') - LENGTH(REPLACE('123-345-566', '-', '')) FROM DUAL;

2
你可以尝试这个:最初的回答。
select count( distinct pos) from
(select instr('123-456-789', '-', level) as pos from dual
  connect by level <=length('123-456-789'))
where nvl(pos, 0) !=0

最初的回答:对于'bbaaaacc'中有多少个'aa',它也会正确计数。
select count( distinct pos) from
(select instr('bbaaaacc', 'aa', level) as pos from dual
  connect by level <=length('bbaaaacc'))
where nvl(pos, 0) !=0

1

这里有一个适用于字符和子字符串的解决方案:

select (length('a') - nvl(length(replace('a','b')),0)) / length('b')
  from dual

其中a是您要搜索b出现的字符串

祝您有美好的一天!


0
SELECT {FN LENGTH('123-345-566')} - {FN LENGTH({FN REPLACE('123-345-566', '#', '')})} FROM DUAL

这是什么类型的语法? - collapsar
您还应提供一些解释。 - Faisal

0
select count(*)
from (
      select substr('K_u_n_a_l',level,1) str
      from dual
      connect by level <=length('K_u_n_a_l')
     )
where str  ='_';

1
虽然这段代码可能回答了问题,但提供有关它如何以及/或为什么解决问题的附加上下文将改善答案的长期价值。 - kayess

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接