Oracle 通过to_number()标识导致异常的行

Oracle 通过to_number()标识导致异常的行,oracle,Oracle,我有一个表,其中包含varchar2之类的门牌号,如10、10a等。我想检测所有不是数字的行,如“10a”。 我的解决方案是,当试图将数据(如“10a”)转换为数字时,输出导致异常的每一行 它应该表明不正确的是10a,但它不是 尝试使用正则表达式,例如: select * from ( select '10a' a from dual union all select '10' a from dual ) where regexp_like(a, '[^[:digi

我有一个表,其中包含varchar2之类的门牌号,如10、10a等。我想检测所有不是数字的行,如“10a”。 我的解决方案是,当试图将数据(如“10a”)转换为数字时,输出导致异常的每一行


它应该表明不正确的是10a,但它不是

尝试使用正则表达式,例如:

select *
from  
(
    select '10a' a from dual
    union all 
    select '10' a from dual
) where regexp_like(a, '[^[:digit:]]');

尝试使用正则表达式,例如:

select *
from  
(
    select '10a' a from dual
    union all 
    select '10' a from dual
) where regexp_like(a, '[^[:digit:]]');

交换这两行的顺序:

number_correct:=to_number(rec.house_nr);
number_incorrect:=rec.house_nr;
按照您让它们进入的顺序,当第一行抛出异常时,第二行被跳过,因此它报告了循环上一次迭代中的错误值,根据定义,这实际上并不错误,因为它成功完成了

我还将删除该行

dbms_output.put_line('correct: '||number_correct);

根据定义,如果to_number调用引发异常,则不会发生对number_correct的赋值,因此在异常处理程序中输出其值似乎毫无意义。

交换这两行的顺序:

number_correct:=to_number(rec.house_nr);
number_incorrect:=rec.house_nr;
按照您让它们进入的顺序,当第一行抛出异常时,第二行被跳过,因此它报告了循环上一次迭代中的错误值,根据定义,这实际上并不错误,因为它成功完成了

我还将删除该行

dbms_output.put_line('correct: '||number_correct);

根据定义,如果to_number调用引发异常,则不会发生对number_correct的赋值,因此在异常处理程序中输出其值似乎毫无意义。

虽然@Dave对您所问的实际问题是正确的,但我认为您试图实现的与您所做的不同。正如所写的那样,PL/SQL块将只计算值,直到它到达第一个非数字。如果要计算所有值,则需要以下内容:

BEGIN
   FOR rec IN (SELECT '10' house_nr FROM DUAL
               UNION ALL
               SELECT '10a' house_nr FROM DUAL
               UNION ALL
               SELECT '11' house_nr FROM DUAL) LOOP
      error_fl         := FALSE;
      BEGIN
         number_correct   := TO_NUMBER(rec.house_nr);
      EXCEPTION
         WHEN VALUE_ERROR THEN
            error_fl   := TRUE;
         WHEN OTHERS THEN
            RAISE;
      END;

      IF error_fl THEN
         DBMS_OUTPUT.put_line('incorrect: ' || rec.house_nr);
      ELSE
         DBMS_OUTPUT.put_line('correct: ' || rec.house_nr);
      END IF;
   END LOOP;
END;
通过将异常处理移动到循环内部,我们可以在引发错误后继续处理。此外,如果发生的错误实际上是转换错误,而不是意外错误,则此版本仅返回错误消息。当您为这样的特定条件编写错误处理时,尽可能精确是很重要的

正如@Stephen指出的,正则表达式通常比PL/SQL过程或函数快,单个SQL语句通常比过程循环好。但是,正则表达式函数只能在where子句中使用,因此如果要查看所有值的结果,需要查询表两次:

WITH test_num as (SELECT '10' house_nr FROM DUAL
                  UNION ALL
                  SELECT '10a' house_nr FROM DUAL
                  UNION ALL
                  SELECT '11' house_nr FROM DUAL)
SELECT 'correct' AS status, a.*
FROM   test_num a
WHERE  REGEXP_LIKE(t1, '[^[:digit:]]')
UNION ALL
SELECT 'incorrect', a.*
FROM   test_num a
WHERE  NOT REGEXP_LIKE(t1, '[^[:digit:]]');
如果您不习惯使用正则表达式,或者无法找到合适的表达式,您仍然可以通过创建自己的函数在单个SQL语句中实现这一点:

CREATE OR REPLACE FUNCTION is_num(p_string VARCHAR2)
   RETURN NUMBER
   DETERMINISTIC IS
   v_test     NUMBER;
BEGIN
   v_test   := p_string;
   RETURN 1;
EXCEPTION
   WHEN VALUE_ERROR THEN
      RETURN 0;
   WHEN OTHERS THEN
      RAISE;
END;

WITH test_num as (SELECT '10' house_nr FROM DUAL
                  UNION ALL
                  SELECT '10a' house_nr FROM DUAL
                  UNION ALL
                  SELECT '11' house_nr FROM DUAL)
SELECT CASE is_num(t1) WHEN 0 THEN 'correct' ELSE 'incorrect' END AS status, a.*
FROM   test_num a;

在大多数情况下,这不会像使用regex那么快,但它确实有一个优点:基于函数的索引对regexp函数非常挑剔,但它们不应该对这样的函数有任何问题。对于这个特定的查询,您似乎不需要索引,但需要记住。

虽然@Dave对您提出的实际问题的回答是正确的,但我认为您试图实现的与您正在做的不同。正如所写的那样,PL/SQL块将只计算值,直到它到达第一个非数字。如果要计算所有值,则需要以下内容:

BEGIN
   FOR rec IN (SELECT '10' house_nr FROM DUAL
               UNION ALL
               SELECT '10a' house_nr FROM DUAL
               UNION ALL
               SELECT '11' house_nr FROM DUAL) LOOP
      error_fl         := FALSE;
      BEGIN
         number_correct   := TO_NUMBER(rec.house_nr);
      EXCEPTION
         WHEN VALUE_ERROR THEN
            error_fl   := TRUE;
         WHEN OTHERS THEN
            RAISE;
      END;

      IF error_fl THEN
         DBMS_OUTPUT.put_line('incorrect: ' || rec.house_nr);
      ELSE
         DBMS_OUTPUT.put_line('correct: ' || rec.house_nr);
      END IF;
   END LOOP;
END;
通过将异常处理移动到循环内部,我们可以在引发错误后继续处理。此外,如果发生的错误实际上是转换错误,而不是意外错误,则此版本仅返回错误消息。当您为这样的特定条件编写错误处理时,尽可能精确是很重要的

正如@Stephen指出的,正则表达式通常比PL/SQL过程或函数快,单个SQL语句通常比过程循环好。但是,正则表达式函数只能在where子句中使用,因此如果要查看所有值的结果,需要查询表两次:

WITH test_num as (SELECT '10' house_nr FROM DUAL
                  UNION ALL
                  SELECT '10a' house_nr FROM DUAL
                  UNION ALL
                  SELECT '11' house_nr FROM DUAL)
SELECT 'correct' AS status, a.*
FROM   test_num a
WHERE  REGEXP_LIKE(t1, '[^[:digit:]]')
UNION ALL
SELECT 'incorrect', a.*
FROM   test_num a
WHERE  NOT REGEXP_LIKE(t1, '[^[:digit:]]');
如果您不习惯使用正则表达式,或者无法找到合适的表达式,您仍然可以通过创建自己的函数在单个SQL语句中实现这一点:

CREATE OR REPLACE FUNCTION is_num(p_string VARCHAR2)
   RETURN NUMBER
   DETERMINISTIC IS
   v_test     NUMBER;
BEGIN
   v_test   := p_string;
   RETURN 1;
EXCEPTION
   WHEN VALUE_ERROR THEN
      RETURN 0;
   WHEN OTHERS THEN
      RAISE;
END;

WITH test_num as (SELECT '10' house_nr FROM DUAL
                  UNION ALL
                  SELECT '10a' house_nr FROM DUAL
                  UNION ALL
                  SELECT '11' house_nr FROM DUAL)
SELECT CASE is_num(t1) WHEN 0 THEN 'correct' ELSE 'incorrect' END AS status, a.*
FROM   test_num a;

在大多数情况下,这不会像使用regex那么快,但它确实有一个优点:基于函数的索引对regexp函数非常挑剔,但它们不应该对这样的函数有任何问题。对于这个特定的查询,您似乎不需要索引,但需要记住。

有效的数字字符串可以包含数字以外的字符。@Dave Costa-我同意。。。但可能不在街道地址中,即门牌号没有句号或负号?我想可能会有一个句号,但是一个负号没有多大意义。即使如此,我仍然认为这个问题是可以用正则表达式解决的,而不是求助于逐行plsql处理。但事实上可能有
包含连字符或斜杠的有效门牌号,其中一些应该是有效的数字,例如10 1/2,但我认为TO_NUMBER也不会接受。有效的数字字符串可以包括数字以外的字符。@Dave Costa-我同意。。。但可能不在街道地址中,即门牌号没有句号或负号?我想可能会有一个句号,但是一个负号没有多大意义。即使如此,我仍然认为这个问题是可以用正则表达式解决的,而不是求助于逐行plsql处理。但实际上可能有包括连字符或斜杠的有效门牌号,其中一些应该可以说是有效的数字,例如10 1/2,尽管我认为TO_NUMBER也不会接受这一点。