使用 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
我知道如果我们直接在 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