2012-03-29 2 views
2

У меня есть таблица с столбцом varchar (A) и еще один целочисленный столбец (B), указывающий тип данных, присутствующих в A. Если B равно 0, тогда A всегда будет содержать числовые цифры.Oracle, где приоритет условия

Так что, когда я сформировать SQL, как этот

SELECT COUNT(*) FROM TAB WHERE B = 0 AND TO_NUMBER(A) = 123; 

я получаю исключение недопустимый номер.

Я ожидаю, что сначала будет оценен B = 0, а затем TO_NUMBER (A) второй, но из приведенного выше сценария я подозреваю, что сначала TO_NUMBER (A) оценивается. Верно ли мое предположение?

+6

см. Http: // stackoverflow.com/q/8900631/272742 – turbanoff

ответ

2

В отличие от языков программирования, таких как C, C#, Java и т. Д., SQL не имеет так называемых условно-логических операторов. Для условных логических операторов правый операнд оценивается только в том случае, если он может влиять на результат. Поэтому оценка && останавливается, если левый операнд возвращает false. Для || он останавливается, если левый операнд возвращает true.

В SQL оба операнда всегда оцениваются. И только оптимизатор запросов выбирает, какой из них оценивается первым.

Я предлагаю вам создать следующую функцию, которая полезна во многих случаях:

FUNCTION IS_NUMBER(P_NUMBER VARCHAR2) 
RETURN NUMBER DETERMINISTIC 
IS 
    X NUMBER; 
BEGIN 
    X := TO_NUMBER(P_NUMBER); 
    RETURN X; 
EXCEPTION 
    WHEN OTHERS THEN RETURN NULL; 
END IS_NUMBER; 

Затем вы можете переписать запрос как:

SELECT COUNT(*) FROM TAB WHERE B = 0 AND IS_NUMBER(A) = 123; 

Вы также можете использовать функцию для проверки строка - это число.

+5

«оба операнда всегда оцениваются» не обязательно верны. Ключевым моментом является то, что операнды (то есть логические выражения в этом случае) оцениваются в произвольном порядке, а не обязательно в порядке их появления в запросе. –

-1

Он должен сначала проверить B = 0.
Я не уверен, что ваша догадка правильная или нет, но не видя данных образца.

SELECT * 
FROM DUAL 
WHERE 1=0 AND 1/0=0 

Вы можете попробовать добавить 1/0 = 0 в качестве последнего оператора (как этот запрос)
на запрос, чтобы узнать, если Oracle замыканиях логический оператор.

sqlfiddle here.

+1

'1 = 0' является константой, оптимизатор уменьшит его до' false', в отличие от 'B = 0', который может быть оценен первым, вторым или параллельно со вторым условием. – Aprillion

+0

@deathApril после дальнейшего чтения, все решение сводится к оптимизатору? так что на самом деле нет способа сделать вывод из запроса без просмотра данных? – cctan

+0

Я не знаю, «нет пути». Это просто, что пример OP не может быть сведен к вашему примеру. – Aprillion

1

вы можете использовать подзапрос, чтобы быть уверенным в правильности результата

select /*+NO_MERGE(T)*/ count(*) 
from (
    select * 
    from TAB 
    where B = 0 
) T 
where TO_NUMBER(A) = 123 
+0

Я не верю, что это гарантирует порядок оценки. Оптимизатор может объединить внешний запрос в встроенный вид, если он считает, что он будет более эффективным. Подсказка «NO_MERGE» может помешать этому. –

+0

Оптимизатор может объединиться, но в этом случае он обязан провести оценку короткого замыкания, в противном случае запросы получаются не эквивалентными. – turbanoff

+0

Я думаю, вы ошибаетесь в этом. Попробуйте 'SELECT count (*) from (выберите * FROM dual, где dummy = 'Y') TAB, где TO_NUMBER (фиктивный) = 123' - это дает мне неправильную ошибку номера. Но если я перехожу к 'SELECT/* + NO_MERGE (tab) */...', он работает без ошибок. –

0

в вашем конкретном примере, вы можете сравнить VARCHARS так:

SELECT COUNT(*) FROM TAB WHERE B = 0 AND A = '123'; 

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

SELECT COUNT(*) FROM TAB WHERE B = 0 AND A = 123; 
+1

Потенциально дает разные результаты - например, если A = '123.0', то сравнение строк завершится неудачно, но сравнение чисел будет успешным. –

2

Вот простой способ заставить чек на B произойдет первое.

SELECT COUNT(*) FROM TAB 
WHERE 123 = DECODE(B, 0, TO_NUMBER(A), NULL); 
Смежные вопросы