2016-04-27 2 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

手動で文字列をtokenisingせずにこれを行うには、XMLとXPath評価で 'トリック' を使用することができます:

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文に連結する必要はありません。

+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

...

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

をあなたは機能は異なる名前を持つ必要があり柔軟であることができるようにしようとしている場合は、あなただけの式を評価したい場合は、なぜドン」:あなたが行うことができます置き換えるとあなたはちょうど - うまく評価していますか?

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;のような文字列を渡すことができます。即時の ''ドロップテーブルx '' ''を実行しますか? –

+0

確かに、私はあなたが迅速かつ汚れた解決に興味があると思った。 SQLインジェクションを避けるために、最初に正規表現を使用して式を検証することをお勧めします。 Alexによって提案されたXMLトリックも安全でなければならない –

関連する問題