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; 

So soll die Ausgabe sein: 23PL/SQL teilen eine Zeichenfolge durch "+" und summieren die Zahlen darin?

+1

Möchten Sie es unbedingt aufteilen, oder wollen Sie wirklich nur die Summe so gut wie möglich auswerten? Wird es immer nur Pluszeichen haben, oder könnte es andere Operatoren, Klammern usw. haben? Werden die Zahlen immer positive ganze Zahlen sein? –

Antwort

8

Sie können einen 'Trick' verwenden, um mit XML und XPath Auswertung, dies zu tun, ohne manuell tokenising die Zeichenfolge:

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

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

Oder:

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

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

Dies ist flexibler, da andere Operatoren verwendet werden können:

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

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

Es mag Teilung nicht, obwohl den normalen /-Operator, und Sie müssen div stattdessen verwenden, so dass Sie einen Ersatz auf der Quellzeichenfolge möglicherweise tun müssen, wenn es einen Schrägstrich enthalten könnte:

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

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

lesen mehr über Oracle's XPath handling und XPath numeric operators.

Sie können das wahrscheinlich direkt anrufen, ohne eine Funktion; aber wenn man vor allem will sie in einer Funktion wickeln, es ist ein wenig komplizierter als die XPath zur Analysezeit festgelegt werden muss, so müßten Sie dynamische SQL verwenden:

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 

ich verwendet habe, the alternative quoting mechanism zu vermeiden die einfachen Anführungszeichen in der Anweisung zu vermeiden, obwohl ich mir nicht sicher bin, dass es hier viel klarer ist. Und ich habe die replace() für den Fall, dass Sie in der Lage sein zu teilen, enthalten, aber Sie wollen nicht, dann verketten Sie einfach direkt in die SQL-Anweisung. Mit ersetzen können Sie tun:

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

Wenn Sie es erlauben werden, flexibel zu sein die Funktion einen anderen Namen haben sollte ...

+2

Schön, niemals daran gedacht (falsch) XPath zu benutzen. –

+0

Danke, aber wie soll ich meinen Code in meine Funktion implementieren? – andrasb

+0

@andrasb - eine Funktion hinzugefügt, die die XMLTable-Methode verwendet. –

1

Sie die Zahlen mit einer hierarchischen Abfrage und einem regulären Ausdruck und die Ergebnisse zusammenzufassen extrahieren können. Etwas wie folgt aus:

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

Wenn Sie nur den Ausdruck bewerten zu können, warum don‘ t du nur - gut - es auswerten?

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

Ich hatte einen ähnlichen Ansatz in der ersten Version meiner Antwort, aber entschied das SQL-Injektionsrisiko unklug gemacht. Ah, aber ich wählte das Ergebnis aus Dual; das ist in gewisser Weise besser, aber du könntest immer noch 'p' machen, was etwas Fieses macht. Du könntest eine Saite wie" 1; execute sofort '' drop table x '' ''zum Beispiel? –

+0

Sicher dachte ich, Sie sind an einer schnellen und schmutzigen Lösung interessiert. Um eine SQL-Injektion zu vermeiden, würde ich vorschlagen, zuerst den Ausdruck zu validieren (z. B. unter Verwendung von regulären Ausdrücken). Die von Alex vorgeschlagenen XML-Tricks sollten ebenfalls sicher sein –

Verwandte Themen