使用 SQL 迭代计算字符串 '6*5+2/8' 的输出

Iteratively Calculate output from String '6*5+2/8' using SQL

我知道如果我们直接在 select 语句中使用它会起作用

select (6*5+2/4) 来自双重;

这将产生输出 30.5,但我的预期是
6*5 = 30 + 2 = 32 / 4 = 8 它应该 return 8

有这样计算的方法吗?

除法优先于加法,因此您的计算默认为:

(6*5)+(2/4)

如果要更改默认优先级,则需要使用括号:

(6*5+2)/4

下面的解决方案假设所有输入都是非负整数,输入表达式中没有括号和空格,并且没有div零偏移。

如果输入有括号,可以先去掉它们。如果有空格,也可以先删除它们。如果输入可能包含小数 and/or 负数,也可以容纳,但需要多做一些工作。

策略:先插入括号强制求值顺序;这显示在 new_str 中间结果中(在 prep 子查询中)。我们还需要将“/”更改为“div”以便在 XQuery 中使用。

然后只需使用 XQuery 计算生成的算术表达式字符串即可。

with
  test_data (str) as (
    select '6*5+2/4'    from dual union all
    select '332'        from dual union all
    select '12+3*5/75'  from dual
  )
, prep (str, new_str) as (
    select str,
           replace(
             rpad('(', length(regexp_replace(str, '\d')), '(') ||
             regexp_replace(str, '([-+*/])', ')')
             , '/', ' div ')
    from   test_data
  )
select str, new_str,
       xmlcast(xmlquery(new_str returning content) as number) as result
from   prep;

STR        NEW_STR               RESULT
---------  -------------------  -------
6*5+2/4    (((6)*5)+2) div 4          8
332        332                      332
12+3*5/75  (((12)+3)*5) div 75        1

您可以使用递归查询:

WITH perform_calculation (value, calculation) AS (
  SELECT value,
         value
  FROM   table_name
UNION ALL
  SELECT value,
         CASE REGEXP_SUBSTR(calculation, '^(-?\d+\.?\d*)([+*/-])(-?\d+\.?\d*)(.*)$', 1, 1, NULL, 2)
         WHEN '+' THEN REGEXP_SUBSTR(calculation, '^(-?\d+\.?\d*)([+*/-])(-?\d+\.?\d*)(.*)$', 1, 1, NULL, 1)
                       +
                       REGEXP_SUBSTR(calculation, '^(-?\d+\.?\d*)([+*/-])(-?\d+\.?\d*)(.*)$', 1, 1, NULL, 3)
         WHEN '-' THEN REGEXP_SUBSTR(calculation, '^(-?\d+\.?\d*)([+*/-])(-?\d+\.?\d*)(.*)$', 1, 1, NULL, 1)
                       -
                       REGEXP_SUBSTR(calculation, '^(-?\d+\.?\d*)([+*/-])(-?\d+\.?\d*)(.*)$', 1, 1, NULL, 3)
         WHEN '*' THEN REGEXP_SUBSTR(calculation, '^(-?\d+\.?\d*)([+*/-])(-?\d+\.?\d*)(.*)$', 1, 1, NULL, 1)
                       *
                       REGEXP_SUBSTR(calculation, '^(-?\d+\.?\d*)([+*/-])(-?\d+\.?\d*)(.*)$', 1, 1, NULL, 3)
         WHEN '/' THEN REGEXP_SUBSTR(calculation, '^(-?\d+\.?\d*)([+*/-])(-?\d+\.?\d*)(.*)$', 1, 1, NULL, 1)
                       /
                       REGEXP_SUBSTR(calculation, '^(-?\d+\.?\d*)([+*/-])(-?\d+\.?\d*)(.*)$', 1, 1, NULL, 3)
         END
         || REGEXP_SUBSTR(calculation, '^(-?\d+\.?\d*)([+*/-])(-?\d+\.?\d*)(.*)$', 1, 1, NULL, 4)
  FROM   perform_calculation
  WHERE  REGEXP_SUBSTR(calculation, '^(-?\d+\.?\d*)([+*/-])(-?\d+\.?\d*)(.*)$', 1, 1, NULL, 1) IS NOT NULL
)
SEARCH DEPTH FIRST BY value SET value_order
SELECT value,
       calculation
FROM   perform_calculation
WHERE  REGEXP_SUBSTR(calculation, '^(-?\d+\.?\d*)([+*/-])(-?\d+\.?\d*)(.*)$', 1, 1, NULL, 1) IS NULL;

其中,对于示例数据:

CREATE TABLE table_name (value) AS
SELECT '6*5+2/4' FROM DUAL UNION ALL
SELECT '6*5+2/4' FROM DUAL UNION ALL
SELECT '32/4*3/2+24/4+3/6' FROM DUAL UNION ALL
SELECT '3/2-3*5' FROM DUAL;

输出:

VALUE CALCULATION
3/2-3*5 -7.5
32/4*3/2+24/4+3/6 2
6*5+2/4 8
6*5+2/4 8

db<>fiddle here