Как посчитать арифметическое выражение, записанное в строке, с помощью подзапросов Oracle SQL
Дана произвольная строка, состоящая из чисел разделённых заданными одинаковыми арифметическими операторами (только «+» или только «-»). Требуется вычислить математическое выражение, записанное таким образом.
Примеры:
Исходная строка: '12+9+45+61' Результат: 127
Исходная строка: '1-12-3' Результат: -14
Важно, что при решении нельзя использовать регулярные выражения, циклы, иерархические запросы и тому подобные вещи. Можно использовать подзапросы (без рекурсии) и функции со строками.
Насколько я понимаю, необходимо разделить числа, входящие в исходную строку и записать их в разные строки, а затем можно выполнить суммирование по значениям в этих строках. Но как это сделать без цикла?
Ответы (2 шт):
Простое решение с функциями со строками - написать свою функцию, принимающую символьное выражение и возвращающую результат его выполнения.
Например (на db<>fiddle):
create table t (id, expr) as
select 1, '12+9+45+61' from dual union all
select 2, '1-12-3' from dual;
with function eval (expr varchar2) return number is
res number;
begin
execute immediate 'begin :r := '||expr||'; end;' using out res;
return res;
end;
select expr, eval (expr)
from t;
Результат:
EXPR EVAL(EXPR)
---------- ----------
12+9+45+61 127
1-12-3 -14
Вот какое решение удовлетворяет всем условиям и решает поставленную задачу
UNDEFINE str
WITH symb_id AS
(SELECT *
FROM (SELECT CASE WHEN LTRIM(REPLACE('&&str',' '),'-') LIKE '%+%'
THEN '+'||LTRIM(REPLACE('&str',' '),'-')||'+'
ELSE '-'||LTRIM(REPLACE('&str',' '),'-')||'-' END nstr
FROM dual) nstr
JOIN (SELECT ROWNUM numb FROM all_objects) numb
ON SUBSTR(nstr.nstr, numb.numb, 1) IN ('+', '-'))
SELECT '&str' "String",
SUM(CASE WHEN ROWNUM = 1
THEN CASE WHEN REPLACE('&str',' ') LIKE '-%'
THEN -ABS(TO_NUMBER(SUBSTR(nstr, id1, id2-id1)))
ELSE ABS(TO_NUMBER(SUBSTR(nstr, id1, id2-id1))) END
ELSE TO_NUMBER(SUBSTR(nstr, id1, id2-id1)) END) "Result"
FROM(SELECT id1.nstr, id1.numb id1, min(id2.numb) id2
FROM symb_id id1 JOIN symb_id id2
ON id1.numb < id2.numb
GROUP BY id1.numb, id1.nstr
ORDER BY id1);