检查Oracle中的“它是否为数字”功能

时间:2011-02-22 18:22:44

标签: oracle plsql oracle10g

我正在尝试检查oracle(10g)查询中的列中的值是否为数字以便进行比较。类似的东西:

select case when ( is_number(myTable.id) and (myTable.id >0) ) 
            then 'Is a number greater than 0' 
            else 'it is not a number' 
       end as valuetype  
  from table myTable

关于如何检查的任何想法?

17 个答案:

答案 0 :(得分:61)

提到here的另一个想法是使用正则表达式来检查:

SELECT  foo 
FROM    bar
WHERE   REGEXP_LIKE (foo,'^[[:digit:]]+$');

好的部分是你不需要单独的PL / SQL函数。可能存在问题的部分是正则表达式可能不是大量行的最有效方法。

答案 1 :(得分:32)

假设myTable中的ID列未声明为NUMBER(这似乎是一个奇怪的选择并且可能有问题),您可以编写一个尝试将(可能是VARCHAR2)ID转换为一个数字,捕获异常,并返回'Y'或'N'。像

这样的东西
CREATE OR REPLACE FUNCTION is_number( p_str IN VARCHAR2 )
  RETURN VARCHAR2 DETERMINISTIC PARALLEL_ENABLE
IS
  l_num NUMBER;
BEGIN
  l_num := to_number( p_str );
  RETURN 'Y';
EXCEPTION
  WHEN value_error THEN
    RETURN 'N';
END is_number;

然后,您可以在查询中嵌入该调用,即

SELECT (CASE WHEN is_number( myTable.id ) = 'Y' AND myTable.id > 0 
               THEN 'Number > 0'
             ELSE 'Something else'
         END) some_alias
  FROM myTable

请注意,尽管PL / SQL具有布尔数据类型,但SQL却没有。因此,虽然您可以声明一个返回布尔值的函数,但您不能在SQL查询中使用这样的函数。

答案 2 :(得分:15)

Saish使用REGEXP_LIKE的答案是正确的想法,但不支持浮动数字。这个会...

返回数字

的值
SELECT  foo 
FROM    bar
WHERE   REGEXP_LIKE (foo,'^-?\d+(\.\d+)?$');

返回值不是数字

SELECT  foo 
FROM    bar
WHERE   NOT REGEXP_LIKE (foo,'^-?\d+(\.\d+)?$');

您可以自己测试正则表达式,直到您的心满意为http://regexpal.com/(但请务必选中在换行符时匹配)。

答案 3 :(得分:5)

这是Finding rows that don't contain numeric data in Oracle的潜在副本。另请参阅:How can I determine if a string is numeric in SQL?

这是一个基于Michael Durrant's的解决方案,适用于整数。

SELECT foo
FROM bar
WHERE DECODE(TRIM(TRANSLATE(your_number,'0123456789',' ')), NULL, 'number','contains char') = 'number'

Adrian Carneiro发布了一个适用于小数和其他的解决方案。但是,正如Justin Cave指出的那样,这会错误地将字符串分类为'123.45.23.234'或'131 + 234'。

SELECT foo
FROM bar
WHERE DECODE(TRIM(TRANSLATE(your_number,'+-.0123456789',' ')), NULL, 'number','contains char') = 'number'

如果您需要没有PL / SQL或REGEXP_LIKE的解决方案,这可能会有所帮助。

答案 4 :(得分:5)

您可以在ORACLE(10g)中使用正则表达式函数'regexp_like',如下所示:

select case
       when regexp_like(myTable.id, '[[:digit:]]') then
        case
       when myTable.id > 0 then
        'Is a number greater than 0'
       else
        'Is a number less than or equal to 0'
     end else 'it is not a number' end as valuetype
from table myTable

答案 5 :(得分:2)

我反对使用when others所以我会使用(因为SQL不支持布尔值而返回“布尔整数”)

create or replace function is_number(param in varchar2) return integer
 is
   ret number;
 begin
    ret := to_number(param);
    return 1; --true
 exception
    when invalid_number then return 0;
 end;

在SQL调用中,您将使用类似

的内容
select case when ( is_number(myTable.id)=1 and (myTable.id >'0') ) 
            then 'Is a number greater than 0' 
            else 'it is not a number or is not greater than 0' 
       end as valuetype  
  from table myTable

答案 6 :(得分:1)

CREATE OR REPLACE FUNCTION is_number(N IN VARCHAR2) RETURN NUMBER IS
  BEGIN
    RETURN CASE regexp_like(N,'^[\+\-]?[0-9]*\.?[0-9]+$') WHEN TRUE THEN 1 ELSE 0 END;
END is_number;

请注意,它不会将45e4视为数字,但您可以随时更改正则表达式以完成相反的操作。

答案 7 :(得分:1)

如何定义列?如果是一个varchar字段,那么它不是一个数字(或存储为一个)。 Oracle可能能够为您进行转换(例如,从someTable中选择*,其中charField = 0),但它只会返回转换成立且可能的行。这也远非理想情况下的表现。

那么,如果您想进行数字比较并将此列视为数字,或许它应该被定义为数字?

那就是说,这就是你可能会做的事情:

create or replace function myToNumber(i_val in varchar2) return number is
 v_num number;
begin
 begin
   select to_number(i_val) into v_num from dual;
 exception
   when invalid_number then
   return null;
 end;
 return v_num;
end;

您可能还包括常规to_number具有的其他参数。用法如下:

select * from someTable where myToNumber(someCharField) > 0;

它不会返回Oracle认为无效数字的任何行。

干杯。

答案 8 :(得分:1)

@JustinCave - "当value_error"替换"当其他人"对您的方法进行了很好的改进。这个轻微的额外调整虽然在概念上是相同的,但不需要为l_num变量定义和后续内存分配:

function validNumber(vSomeValue IN varchar2)
     return varchar2 DETERMINISTIC PARALLEL_ENABLE
     is
begin
  return case when abs(vSomeValue) >= 0 then 'T' end;
exception
  when value_error then
    return 'F';
end;

对于任何喜欢使用"风险较高的"模拟Oracle数字格式逻辑的人也要注意。 REGEXP方法,请不要忘记考虑NLS_NUMERIC_CHARACTERS和NLS_TERRITORY。

答案 9 :(得分:1)

这是我查询所有非数字的查询:

Select myVarcharField
From myTable
where not REGEXP_LIKE(myVarcharField, '^(-)?\d+(\.\d+)?$', '')
and not REGEXP_LIKE(myVarcharField, '^(-)?\d+(\,\d+)?$', '');

在我的领域,我。并且,遗憾的是十进制数不得不考虑到这一点,否则你只需要一个限制。

答案 10 :(得分:0)

好吧,您可以创建is_number函数来调用,以便您的代码正常工作。

create or replace function is_number(param varchar2) return boolean
 as
   ret number;
 begin
    ret := to_number(param);
    return true;
 exception
    when others then return false;
 end;
编辑:请按照贾斯汀的回答。忘了纯SQL调用的细节......

答案 11 :(得分:-1)

长度为10位的手机号码的功能,使用正则表达式从9,8,7开始

create or replace FUNCTION VALIDATE_MOBILE_NUMBER
(   
   "MOBILE_NUMBER" IN varchar2
)
RETURN varchar2
IS
  v_result varchar2(10);

BEGIN
    CASE
    WHEN length(MOBILE_NUMBER) = 10 
    AND MOBILE_NUMBER IS NOT NULL
    AND REGEXP_LIKE(MOBILE_NUMBER, '^[0-9]+$')
    AND MOBILE_NUMBER Like '9%' OR MOBILE_NUMBER Like '8%' OR MOBILE_NUMBER Like '7%'
    then 
    v_result := 'valid';
    RETURN v_result;
      else 
      v_result := 'invalid';
       RETURN v_result;
       end case;
    END;

答案 12 :(得分:-1)

我相信以下解决方案(基于上面的regexp_like方法)是最佳的:

function isInteger(vYourValue IN varchar2)
         return varchar2
         is
begin
  return case REGEXP_INSTR(vYourValue,'^[[:digit:]]+$') when 0 then 'F' else 'T' end;
end;

我为什么这么说?

  1. 通过适当更改正则表达式,可以根据需要修改测试的数字子集。

  2. 它可以在SQL中使用,即从mytable中选择isInteger(myCol);因为它返回'F'的'T'而不是布尔值。

  3. 它可以在pl / sql中原生使用ie。 if isInteger(vMyValue)='T'然后....

  4. 它满足WNDS,WNPS纯度断言。

  5. 在我看来,它不依赖于“当其他人”接近结果确定时的过于宽泛的范围。

答案 13 :(得分:-1)

请注意,正则表达式或函数方法是several times slower than plain sql condition

因此,一些具有有限适用性的启发式变通方法可以进行大量扫描。

如果您确定非数字值包含一些字母,则会出现solution

select case when upper(dummy)=lower(dummy) then '~numeric' else '~alpabetic' end from dual

如果你知道某些字母总是出现在非数字的情况下:

select case when instr(dummy, 'X')>0 then '~alpabetic' else '~numeric' end from dual

当数字案例总是包含零时:

select case when instr(dummy, '0')=0 then '~alpabetic' else '~numeric' end from dual

答案 14 :(得分:-1)

如果condition为null则为数字

IF(rtrim(P_COD_LEGACY, '0123456789') IS NULL) THEN
                return 1;
          ELSE
                return 0;
          END IF;

答案 15 :(得分:-1)

您可以使用此示例

SELECT NVL((SELECT 1 FROM  DUAL WHERE   REGEXP_LIKE (:VALOR,'^[[:digit:]]+$')),0) FROM DUAL;

答案 16 :(得分:-1)

这是一个简单的方法:

  • 不依靠TRIM
  • 不依赖REGEXP
  • 允许指定小数和/或千位分隔符(在我的示例中为“。”和“,”)
  • 在早于8i的Oracle版本上(在8.1.7.4.0上进行个人测试;是的,您没看错)
SELECT
    TEST_TABLE.*,

    CASE WHEN
        TRANSLATE(TEST_TABLE.TEST_COLUMN, 'a.,0123456789', 'a') IS NULL
    THEN 'Y'
    ELSE 'N'
    END
    AS IS_NUMERIC

FROM
    (
    -- DUMMY TEST TABLE
        (SELECT '1' AS TEST_COLUMN FROM DUAL) UNION
        (SELECT '1,000.00' AS TEST_COLUMN FROM DUAL) UNION
        (SELECT 'xyz1' AS TEST_COLUMN FROM DUAL) UNION
        (SELECT 'xyz 123' AS TEST_COLUMN FROM DUAL) UNION
        (SELECT '.,' AS TEST_COLUMN FROM DUAL)
    ) TEST_TABLE

结果:

TEST_COLUMN IS_NUMERIC
----------- ----------
.,          Y
1           Y
1,000.00    Y
xyz 123     N
xyz1        N

5 rows selected.

当然,这可能不是最强大的方法。例如“。”被错误地标识为数字。但是,它非常简单,快速,并且可能非常适合完成此工作,具体取决于需要处理的实际数据值。

对于整数,我们可以简化Translate操作,如下所示:

TRANSLATE(TEST_TABLE.TEST_COLUMN, 'a0123456789', 'a') IS NULL

工作方式

从上面开始,请注意Translate函数的语法为TRANSLATE(string, from_string, to_string)。现在Translate函数不能接受NULL作为to_string参数。 因此,通过将'a0123456789'指定为from_string,将'a'指定为to_string,会发生两件事:

  • 字符a被保留;
  • 将数字09替换为空,因为在to_string中没有为它们指定任何替换。

实际上,数字被丢弃。如果该操作的结果为NULL,则表示它纯粹是数字开头。