Нет короткого замыкания ИЛИ с функцией Oracle? - PullRequest
1 голос
/ 01 сентября 2011

Чтобы разрешить суперпользователю / администратору войти в мою систему, я выполняю (увеличенную версию) этот запрос:

Select *
  From mytable
 Where (:id = 'Admin' Or :id = mytable.id);

Если я передаю идентификатор пользователя, я получаю все данные дляэтот пользователь;если я передаю строку «Admin», я получаю все данные.Это работает, потому что OR в Oracle является оператором короткого замыкания .

Однако, если я сделаю 'Admin' константой пакета и получу ее с помощью функции, такой как

Select *
  From mytable
 Where (:id = mypackage.GetAdminConstant Or :id = mytable.id);

Я получаю ORA-01722: invalid number, когда передаю 'Admin'.

Почему ИЛИ теряет свой аспект короткого замыкания, когда я ввожу функцию?

Ответы [ 2 ]

6 голосов
/ 01 сентября 2011

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

В C, если вы пишете a || b, вы знаете, что сначала будет оцениваться a, затем b будет оцениваться только при необходимости.

В SQL, если вы пишете a OR b, вы знаете только то, что сначала будет оцениваться либо a, либо b, а другое выражение (по крайней мере, в Oracle) будет оцениваться только при необходимости.

Просмотр плана выполнения для двух запросов может дать некоторое представление о порядке оценки, а может и нет.

Я бы предположил, что в вашем первом случае Oracle видит, что первое выражение будет иметь одинаковое значение для каждой строки, поэтому сначала оценивает его. Когда вы переходите ко второму случаю, Oracle теперь видит функцию, которая может иметь разные результаты при каждой оценке, поэтому ему придется проверять каждую строку, поэтому он пытается выполнить простую проверку на равенство для столбца перед выполнением вызова функции. .

Интересно, получили бы вы другие результаты, если бы отметили функцию DETERMINISTIC, чтобы Oracle знал, что она, по сути, является константой.

1 голос
/ 05 сентября 2011

Лучше использовать 2 переменные связывания.

Select *
  From mytable
 Where (:admin = 'Admin' Or (:admin is null and :id = mytable.id));
...