2016-04-27 22 views
2
CREATE OR REPLACE FUNCTION sumNumbers(p VARCHAR2) RETURN NUMBER IS 

... 

SELECT sumNumbers('3 + 6 + 13 + 0 + 1') FROM dual; 

所以输出应该是:23PL/SQL用“+”分割一个字符串并对其中的数字求和?

+1

你是否一定要分裂它,或者你真的只是想要评估任何方式,你可以?它总是只有加号,还是可以有其他操作符,括号等?数字总是正整数吗? –

回答

8

您可以使用一个 '窍门' 与XML和XPath评估要做到这一点,而无需手动tokenising字符串:

select * from xmltable('3 + 6 + 13 + 0 + 1' columns result number path '.'); 

    RESULT 
---------- 
     23 

或者:

select to_number(xmlquery('3 + 6 + 13 + 0 + 1' returning content).getStringVal()) as result 
from dual; 

    RESULT 
---------- 
     23 

这是作为其他运营商可以使用更灵活:

select * from xmltable('2 * (5 + 7) - 3' columns result number path '.'); 

    RESULT 
---------- 
     21 

使用正常/运营商,虽然它不喜欢分裂,你需要使用div代替,所以你可能需要做您的源字符串替换,如果它可能包含一个斜杠:

select * from xmltable('2 * (5 + 7) div 3' columns result number path '.'); 

    RESULT 
---------- 
     8 

阅读更多关于Oracle's XPath handling和​​。

你可以直接调用它,而不需要函数;但如果你特别想将其包装在一个函数这是一个稍微复杂的XPath的必须固定在分析时,那么你就需要使用动态SQL:

CREATE OR REPLACE FUNCTION sumNumbers(p VARCHAR2) RETURN NUMBER IS 
    l_result NUMBER; 
BEGIN 
    execute immediate q'[select * from xmltable(']' 
    || replace(p, '/', ' div ') 
    || q'[' columns result number path '.')]' 
    into l_result; 

    return l_result; 
END; 
/

SELECT sumNumbers('3 + 6 + 13 + 0 + 1') FROM dual; 

       SUMNUMBERS('3+6+13+0+1') 
--------------------------------------- 
            23 

我用the alternative quoting mechanism避免逃避声明中的单引号,尽管我不确定它在这里更清晰。并且我已经包含了replace()以防需要分割,但它不需要那么直接将p直接连接到SQL语句中。有了替代你可以这样做:

SELECT sumNumbers('2 * (5 + 7)/3') FROM dual; 

如果你要允许其灵活的功能应该有不同的名称...

+2

不错,从来没有想过(错) - 以这种方式使用XPath。 –

+0

谢谢,但我应该如何实现你的代码到我的函数? – andrasb

+0

@andrasb - 添加了一个使用XMLTable方法的函数。 –

1

您可以用层次查询和正则表达式中提取的数字,总结的结果。事情是这样的:

WITH t AS (
    SELECT '3 + 6 + 13 + 0 + 1' numbers FROM DUAL 
) 
SELECT SUM(TRIM(REGEXP_SUBSTR(numbers,'(?[[:digit:]]+ ?)',1,LEVEL))) 
FROM t 
CONNECT BY LENGTH(SUBSTR(numbers,DECODE(REGEXP_INSTR(numbers,'(?[[:digit:]]+ ?)',1,LEVEL),0,NULL,REGEXP_INSTR(numbers,'(?[[:digit:]]+ ?)',1,LEVEL)))) <= LENGTH(SUBSTR(numbers,DECODE(REGEXP_INSTR(numbers,'(?[[:digit:]]+ ?)',1,LEVEL),0,NULL,REGEXP_INSTR(numbers,'(?[[:digit:]]+ ?)',1,LEVEL)))); 
0

如果你只是想试用的表达,为什么不”你只是 - 好 - 评估它?

FUNCTION sumNumbers(p VARCHAR2) RETURN NUMBER IS 
     l_result NUMBER; 
BEGIN 
     EXECUTE IMMEDIATE 'begin :n := ' || p || '; end;' 
      USING OUT l_result; 
     RETURN l_result; 
END; 
+0

我在答案的第一个版本中采用了类似的方法,但确定SQL注入风险并不明智。啊,但我选择了双重结果;这在某种程度上更好,但是你仍然可以用'p'来做一些令人讨厌的事情。你可以像'1;立即执行''drop table x''''例如? –

+0

当然,我以为你对快速和肮脏的解决方案感兴趣。为了避免SQL注入,我建议首先验证表达式(例如使用正则表达式)。 Alex建议的xml技巧也应该是安全的 –