2016-04-27 25 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技巧也應該是安全的 –