Определение строк, вызывающих исключения, с помощью to_number () - PullRequest
0 голосов
/ 09 августа 2011

У меня есть таблица, содержащая номера домов как 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, но это не так.т.

Ответы [ 3 ]

5 голосов
/ 09 августа 2011

Хотя @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;

Это не так быстро, как использование регулярных выражений в большинстве случаев, но у него есть одно преимущество: основанные на функциях индексы общеизвестно привередливы к функциям регулярных выражений, но у них не должно быть проблем с такой функцией. Не похоже, что вам нужен индекс для этого конкретного запроса, но об этом следует помнить.

2 голосов
/ 09 августа 2011

Поменяйте местами порядок этих двух строк:

number_correct:=to_number(rec.house_nr);
number_incorrect:=rec.house_nr;

В том порядке, в котором вы их используете, когда первая строка выдает исключение, вторая строка пропускается, поэтому она сообщает «неверное» значение из предыдущей итерации цикла (которое, по определению, на самом деле не неверно, поскольку успешно завершено)

Я бы тоже убрал строку

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

По определению, если вызов to_number() вызывает исключение, присваивание number_correct не произойдет, поэтому выводить его значение в обработчике исключений кажется бессмысленным.

1 голос
/ 09 августа 2011

Попробуйте использовать регулярные выражения, например:

select *
from  
(
    select '10a' a from dual
    union all 
    select '10' a from dual
) where regexp_like(a, '[^[:digit:]]');
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...