【问题标题】:check if "it's a number" function in Oracle检查Oracle中的“它是否是一个数字”功能
【发布时间】:2011-07-02 05:11:45
【问题描述】:

我正在尝试检查 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

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

【问题讨论】:

标签: oracle plsql oracle10g


【解决方案1】:

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

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

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

【讨论】:

  • 非常聪明的解决方案。我不知道 REGEXP_LIKE :)。谢谢
  • 很高兴您发现它有帮助。我不能相信。 OP在上面链接。不过谢谢。
  • 要包含负数,请使用 REGEXP_LIKE (foo, '^-?[[:digit]]+$') 要匹配浮点数,请参阅 Matt Byrnes 的答案。
【解决方案2】:

假设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 查询中使用这样的函数。

【讨论】:

  • 为什么不用 1 和 0 而不是 'Y' 和 'N'? PL/SQL 具有 NUMBER 数据类型,SQL 将在查询中接受 1 和 0。
  • @eyetea - 个人喜好。您当然可以返回 0 或 1。如果开发人员都是说英语的,我倾向于使用 Y 和 N,因为这样更能自我说明。如果您正在处理更多的国际化开发,那么通过将语言排除在外,0 和 1 更有意义。
  • 我只是说在0和1的情况下,不需要写is_number(myTable.id)=1,但是is_number(myTable.id)就足够了!
  • @eyetea - 这不是真的。在 PL/SQL 中,0 不是隐含的假,1 不是隐含的真。您仍然需要在表达式中包含 = 1 部分才能生成布尔结果。
  • @Reimius - 这将取决于数据实际上是数字的频率。异常通常很昂贵,因此如果大多数数据都是非数字的,那么这种方法将有些低效。另一方面,如果大多数数据是数字的,则此函数非常有效。这也处理了非整数,而 Saish 的正则表达式不允许也不允许像 1.234.567.890 这样的非整数,比如 kevlened 的解决方案。它尊重您会话的小数点分隔符。
【解决方案3】:

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/ 感到满意为止(但请确保您为此选择了ma​​tch at line breaks复选框)。

【讨论】:

  • 为了包含负数,表达式应该是 REGEXP_LIKE (foo,'^-?\d+(\.\d+)?$');
  • 是的,好点 - 我在我的正则表达式中粘贴了我正在做的事情,我只想要正数。会更新我的答案 - 喜欢这个名字 btw shonky ;-)
  • .1+11234f1e1 怎么样?并非所有这些通常都被认为是“数字”,但它们都是 Oracle 的有效数字。
  • 如果它们是“Oracle 的数字”,那么它们将被存储为数字。由于问题是关于 VARCHAR/VARCHAR2/whatever 的问题,因此问题更多的是关于生成数字的系统/代码,而不一定是 Oracle 本身(想象一下 Oracle 之外的世界)。在这种情况下,您可以随心所欲地迂腐,也可以尝试适应手头的用例。评论是公平的,没有必要投反对票。
  • 需要这个投票,因为这是最好的答案!
【解决方案4】:

这是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 的解决方案,这可能会有所帮助。

【讨论】:

  • 我在一个简化的变体中使用了这个解决方案:
    SELECT foo FROM bar WHERE TRANSLATE(your_number, 'X0123456789','X') IS NULL
    请注意,X 没有被翻译。使用它只是因为第二个字符串不能是空字符串。
【解决方案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

【讨论】:

    【解决方案6】:

    我反对使用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
    

    【讨论】:

    • invalid_number 是错误的例外。这是 SQL 语句的例外,但在 PL/SQL 中例外是 value_error
    【解决方案7】:

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

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

    在我的领域,我有 .并且,十进制数字很遗憾,所以必须考虑到这一点,否则你只需要一个限制。

    【讨论】:

    • 很高兴有一个被投反对票的 cmets。
    【解决方案8】:

    列是如何定义的?如果它是一个 varchar 字段,那么它不是一个数字(或存储为一个)。 Oracle 可能能够为您进行转换(例如,select * from someTable where 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 视为无效数字的行。

    干杯。

    【讨论】:

      【解决方案9】:
      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 视为数字,但您可以随时更改正则表达式来完成相反的操作。

      【讨论】:

        【解决方案10】:

        @JustinCave - 用“when value_error”替换“when others”是对上述方法的一个很好的改进。这个微小的额外调整,虽然在概念上是相同的,但消除了对 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;
        

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

        【讨论】:

        • 很高兴您在原始答案中提到了纯度规则。它让我想起了在并行 SQL 中使用这些函数时遇到的问题,并通过添加 DETERMINISTICPARALLEL_ENABLE 帮助改进了公认的答案。然而,这个版本并不比原来的功能“更纯粹”。不写入包状态不适用,因为不涉及包变量。并且简单地“更纯粹”并没有帮助,必须声明它以允许并行性等功能工作。
        • @jonearles - 贾斯汀的解决方案包含一个变量“l_num”,我将其解释为“包变量”,但是在测试中我发现断言不受函数变量的影响。
        • @jonearles - 我有一些回应我将分成不同的 cmets 1. Justin 的解决方案包含一个变量“l_num”,我将其解释为“包变量”但是在测试中我看到断言是不受函数变量的影响 - 所以它确实不纯。
        • @jonearles 关于您的 DETERMINISTIC 观察:实现和实施之间存在差异。每个用例的实现都是可选的,但由于没有实现是不可能的,所以我所指的实现是至关重要的。此外,我的函数位于包体中,并且断言是通过 RESTRICT_REFERENCES pragma 在规范中完成的。
        • @jonearles - 尽管我的函数确实不是更纯粹,但它仍然是我的首选,因为不需要定义和特定内存分配给贾斯汀解决方案中包含的本质上不相关的 l_num 变量.
        【解决方案11】:

        好吧,你可以创建 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 调用的那个小细节......

        【讨论】:

        • 如果您只需要在 PL/SQL 中进行检查,则返回布尔值的函数会很好地工作。但是不能(遗憾地)从 SQL 查询中调用返回布尔值的函数,因为 SQL 引擎没有布尔数据类型。
        • 是的,这就是我在没有测试的情况下潜水的结果! ;)
        【解决方案12】:

        你可以使用这个例子

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

        【讨论】:

          【解决方案13】:

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

          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;
          

          【讨论】:

            【解决方案14】:

            请注意,正则表达式或函数方法是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
            

            【讨论】:

              【解决方案15】:

              如果条件为空则为数字

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

              【讨论】:

                【解决方案16】:

                这是一个简单的方法:

                • 不依赖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,则表示它最初是纯数字。

                【讨论】:

                  猜你喜欢
                  • 2017-10-01
                  • 2012-10-23
                  • 2015-09-15
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-03-15
                  相关资源
                  最近更新 更多