Есть ли встроенная функция выполняющая динамическое выражение?
Есть ли в БД встроенная функция, которая работает подобным образом:
CREATE OR REPLACE FUNCTION IFTrueValue(iValue NUMBER, sOper VARCHAR2, iTest NUMBER, iTrue NUMBER) RETURN NUMBER AS
bCond BOOLEAN;
BEGIN
EXECUTE IMMEDIATE '
SELECT CASE WHEN '||iValue||sOper||iTest||' THEN 1 ELSE 0 END
FROM dual' INTO bCond;
RETURN CASE bCond
WHEN TRUE THEN iTrue
ELSE iValue END;
END;
/
SELECT IFTrueValue(Mod(20, 20), '=', 0, 20) res FROM dual; -- вывод 20
SELECT IFTrueValue(Mod(25, 20), '=', 0, 20) res FROM dual; -- вывод 5: Mod(25, 20)
Функция работает, но интересно, есть ли встроенный в БД функционал.
Свободный перевод вопроса Oracle test function от участника @Michał Barbarski
Ответы (2 шт):
Можно воспользоватся встроеными функциями xmlCast() и xmlQuery() для этой цели.
Вот воспроизводимый пример с тестовыми дданными:
with t(iValue, sOper, iTest, iTrue) as (
select Mod(20, 20), '=', 0, 20 from dual union all
select Mod(25, 20), '=', 0, 20 from dual union all
select Mod(25, 20), '>', 0, 13 from dual
)
select t.*, xmlcast (xmlquery (('
if ($iValue '||sOper||' $iTest) then $iTrue else $iValue')
passing iValue as "iValue", iTest as "iTest", iTrue as "iTrue"
returning content) as number) result
from t
/
Результат:
IVALUE SOPER ITEST ITRUE RESULT
---------- ----- ---------- ---------- ----------
0 = 0 20 20
5 = 0 20 5
5 > 0 13 13
Свободный перевод ответа от участника @Sayan Malakshinov
Выражение WHEN '||iValue||sOper||iTest||' THEN как минимум должно использовать переменные связывания, а не конкантинировать значения. Для условных операторов это невозможно. Но их то как раз не так много, поэтому наиболее эффективно отказаться от динамического выполнения выражений, а использовать просто CASE.
Вот пример того, что имею ввиду:
with function testExpression (
expr number, testoper char, value number, intruth number) return number is
function raiseIllegalOper return char is
begin raise_application_error (-20000, 'illegal test operator '||testoper);
end;
begin
return case testoper
when '=' then case expr = value when true then intruth else expr end
when '>' then case expr > value when true then intruth else expr end
-- when ... итд для ['<', '<=', '>=', '!=']
else raiseIllegalOper end;
end;
t (expr, testoper, value, intruth) as (
select mod (20, 20), '=', 0, 20 from dual union all
select mod (25, 20), '=', 0, 20 from dual union all
select mod (25, 20), '>', 0, 13 from dual
)
select t.*, testExpression (expr, testoper, value, intruth) result
from t
/
Результат:
EXPR TESTOPER VALUE INTRUTH RESULT
---------- -------- ---------- ---------- ----------
0 = 0 20 20
5 = 0 20 5
5 > 0 13 13