К сожалению, различные предложенные подходы подзапроса не гарантируют работу. Oracle может вставить предикат в подзапрос, а затем оценивать условия в любом порядке, который он сочтет целесообразным. Если произойдет оценка условия PERSON_UID
перед фильтрацией нечисловых строк, вы получите ошибку. У Джонатана Дженника есть отличная статья Subquery Madness , в которой эта проблема обсуждается довольно подробно.
Это оставляет вам несколько вариантов
1) Переработать модель данных. Как правило, не рекомендуется хранить числа в столбцах NUMBER. Помимо возникновения такого рода проблем, он имеет тенденцию искажать оценки количества элементов оптимизатора, что приводит к неоптимальным планам запросов.
2) Измените условие, указав строковое значение, а не число. Если предполагается, что PERSON_UID
является строкой, ваше условие фильтрации может быть PERSON_UID = '100'
. Это позволяет избежать необходимости выполнять неявное преобразование.
3) Напишите пользовательскую функцию, которая выполняет преобразование строки в число, игнорирует все ошибки и использует ее в своем коде, т.е.
CREATE OR REPLACE FUNCTION my_to_number( p_arg IN VARCHAR2 )
RETURN NUMBER
IS
BEGIN
RETURN to_number( p_arg );
EXCEPTION
WHEN others THEN
RETURN NULL;
END;
, а затем my_to_number(PERSION_UID) = 100
4) Используйте подзапрос, который предотвращает выдвижение предиката. Это можно сделать несколькими разными способами. Лично я предпочитаю бросать ROWNUM в подзапрос, то есть опираться на решение OMG Ponies
WITH valid_persons AS (
SELECT TO_NUMBER(c.person_uid) 'person_uid',
ROWNUM rn
FROM S_CONTACT c
WHERE REGEXP_LIKE(c.personuid, '[[:digit:]]'))
SELECT *
FROM valid_persons vp
WHERE vp.person_uid = 100
Oracle не может вставить предикат vp.person_uid = 100
в подзапрос, потому что это изменило бы результаты. Вы также можете использовать подсказки для принудительной материализации подзапроса или для предотвращения нажатия предикатов.