我有一个包含门牌号码的表格,如varchar2,如10,10a等。我想检测所有非数字的行,如'10a'。
我的解决方案是在尝试将“10a”等数据转换为数字时输出导致异常的每一行。
declare
number_correct number;
number_incorrect varchar2(4000);
begin
for rec in(select '10' house_nr from dual union select '10a' house_nr from dual)
loop
number_correct:=to_number(rec.house_nr);
number_incorrect:=rec.house_nr;
end loop;
exception
when others then
dbms_output.put_line('correct: '||number_correct);
dbms_output.put_line('incorrect: '||number_incorrect);
end;
它应该表明不正确是10a,但它没有。
答案 0 :(得分:5)
虽然@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;
这在大多数情况下都不如使用正则表达式那么快,但它确实有一个优点:基于函数的索引对于regexp函数非常挑剔,但它们不应该对这样的函数有任何问题。看起来你不需要这个特定查询的索引,但要注意这一点。
答案 1 :(得分:2)
交换这两行的顺序:
number_correct:=to_number(rec.house_nr);
number_incorrect:=rec.house_nr;
使用它们的顺序,当第一行抛出异常时,跳过第二行,因此它报告循环的前一次迭代中的“不正确”值(根据定义,实际上不是不正确,因为它成功完成了。)
我也会删除该行
dbms_output.put_line('correct: '||number_correct);
根据定义,如果to_number()
调用抛出异常,则number_correct
的赋值将不会发生,因此在异常处理程序中输出其值似乎毫无意义。
答案 2 :(得分:1)
尝试使用正则表达式,例如:
select *
from
(
select '10a' a from dual
union all
select '10' a from dual
) where regexp_like(a, '[^[:digit:]]');