2010-06-01 17 views
20

Sto utilizzando la funzione REPLACE in oracle per sostituire i valori nella mia stringa come;Funzione REPLACE multipla in Oracle

SELECT REPLACE('THE NEW VALUE IS #VAL1#','#VAL1#','55') from dual 

Quindi questo è OK per sostituire un valore, ma per quanto riguarda 20+, devo usare 20+ REPLACE funzione o c'è una soluzione più pratica.

Tutte le idee sono benvenute.

risposta

31

Anche se questo thread è vecchio è il primo su Google, quindi pubblicherò un equivalente Oracle per la funzione implementata qui, utilizzando le espressioni regolari.

È abbastanza più veloce di nested replace() e molto più pulito.

Per sostituire stringhe 'a', 'b', 'c' con 'd' in una colonna stringa da una determinata tabella

select regexp_replace(string_col,'a|b|c','d') from given_table 

è altro che un'espressione regolare per diversi schemi statici con 'o' operatore.

Attenzione ai caratteri speciali regexp!

+0

Questa semplice soluzione è molto meglio che scrivere le proprie funzioni facendo ciò che è già stato implementato. – Tosz

+0

Sono d'accordo che questa è la risposta migliore per quanto riguarda la domanda vera e propria – dangel

+0

Molto meglio! Molte grazie! – GeorgiG

20

La risposta accettata a how to replace multiple strings together in Oracle suggerisce di utilizzare le istruzioni nidificate REPLACE e non penso che esista un modo migliore.

Se avete intenzione di fare un uso pesante di questo, si potrebbe non crei una propria funzione:

CREATE TYPE t_text IS TABLE OF VARCHAR2(256); 

CREATE FUNCTION multiple_replace(
    in_text IN VARCHAR2, in_old IN t_text, in_new IN t_text 
) 
    RETURN VARCHAR2 
AS 
    v_result VARCHAR2(32767); 
BEGIN 
    IF(in_old.COUNT <> in_new.COUNT) THEN 
    RETURN in_text; 
    END IF; 
    v_result := in_text; 
    FOR i IN 1 .. in_old.COUNT LOOP 
    v_result := REPLACE(v_result, in_old(i), in_new(i)); 
    END LOOP; 
    RETURN v_result; 
END; 

e quindi utilizzarlo in questo modo:

SELECT multiple_replace('This is #VAL1# with some #VAL2# to #VAL3#', 
         NEW t_text('#VAL1#', '#VAL2#', '#VAL3#'), 
         NEW t_text('text', 'tokens', 'replace') 
         ) 
FROM dual 

Questo è il testo con alcuni token per sostituire

Se tutti i token hanno lo stesso formato ('#VAL' || i || '#'), è possibile omettere il parametro in_old e utilizzare invece il contatore di loop.

+0

Grazie Peter Lang – Adnan

+0

@Adnan: Di niente, ma mi avrebbe aspettato per un po 'di tempo prima di accettare la mia risposta - Ci potrebbero essere altre/risposte migliori là fuori :) –

+0

'RETURN VARCHAR2 DETERMINISTIC' potrebbe essere probabilmente più veloce. – Benoit

16

Tenete a mente le conseguenze

SELECT REPLACE(REPLACE('TEST123','123','456'),'45','89') FROM DUAL; 

sostituirà il 123 con 456, poi scoprire che si può sostituire il 45 con 89. Per una funzione che ha avuto un risultato equivalente, avrebbe dovuto duplicare il precedenza (cioè sostituendo le stringhe nello stesso ordine).

Analogamente, prendere una stringa "ABCDEF" e istruirlo in modo da sostituire "ABC" con "123" e "CDE" con "xyz" dovrebbe comunque tenere conto di una precedenza per determinare se è andato a "123EF" o ABxyzF '.

In breve, sarebbe difficile trovare qualcosa di generico che sarebbe più semplice di una SOSTITUZIONE annidata (anche se qualcosa che era più una funzione di stile sprint sarebbe stata un'utile aggiunta).

3

Questo è un vecchio post, ma ho finito per usare i pensieri di Peter Lang, e ho fatto un approccio simile, ma diverso. Ecco quello che ho fatto:

CREATE OR REPLACE FUNCTION multi_replace(
         pString IN VARCHAR2 
         ,pReplacePattern IN VARCHAR2 
) RETURN VARCHAR2 IS 
    iCount INTEGER; 
    vResult VARCHAR2(1000); 
    vRule VARCHAR2(100); 
    vOldStr VARCHAR2(50); 
    vNewStr VARCHAR2(50); 
BEGIN 
    iCount := 0; 
    vResult := pString; 
    LOOP 
     iCount := iCount + 1; 

     -- Step # 1: Pick out the replacement rules 
     vRule := REGEXP_SUBSTR(pReplacePattern, '[^/]+', 1, iCount); 

     -- Step # 2: Pick out the old and new string from the rule 
     vOldStr := REGEXP_SUBSTR(vRule, '[^=]+', 1, 1); 
     vNewStr := REGEXP_SUBSTR(vRule, '[^=]+', 1, 2); 

     -- Step # 3: Do the replacement 
     vResult := REPLACE(vResult, vOldStr, vNewStr); 

     EXIT WHEN vRule IS NULL; 
    END LOOP; 

    RETURN vResult; 
END multi_replace; 

allora posso usare in questo modo:

SELECT multi_replace(
         'This is a test string with a #, a $ character, and finally a & character' 
         ,'#=%23/$=%24/&=%25' 
     ) 
FROM dual 

Questo fa in modo che io can can qualsiasi carattere/stringa con qualsiasi carattere/stringa.

Ho scritto un post su questo sul mio blog.

4

Nel caso in cui tutte le corde di origine e di sostituzione sono giusto il tempo di un carattere, si può semplicemente utilizzare la funzione TRANSLATE:

SELECT translate('THIS IS UPPERCASE', 'THISUP', 'thisup') 
    FROM DUAL 

Vedi the Oracle documentation per i dettagli.

1

Ho creato una funzione di stringa generale multi-sostituzione Oracle da una tabella di varchar2 come parametro. Il varchar verrà sostituito per il valore di posizione rownum della tabella.

Ad esempio:

Text: Hello {0}, this is a {2} for {1} 
Parameters: TABLE('world','all','message') 

Returns:

Hello world, this is a message for all. 

È necessario creare un tipo:

CREATE OR REPLACE TYPE "TBL_VARCHAR2" IS TABLE OF VARCHAR2(250); 

Il funcion è:

CREATE OR REPLACE FUNCTION FN_REPLACETEXT(
    pText IN VARCHAR2, 
    pPar IN TBL_VARCHAR2 
) RETURN VARCHAR2 
IS 
    vText VARCHAR2(32767); 
    vPos INT; 
    vValue VARCHAR2(250); 

    CURSOR cuParameter(POS INT) IS 
    SELECT VAL 
     FROM 
      (
      SELECT VAL, ROWNUM AS RN 
      FROM (
        SELECT COLUMN_VALUE VAL 
        FROM TABLE(pPar) 
       ) 
      ) 
     WHERE RN=POS+1; 
BEGIN 
    vText := pText; 
    FOR i IN 1..REGEXP_COUNT(pText, '[{][0-9]+[}]') LOOP 
     vPos := TO_NUMBER(SUBSTR(REGEXP_SUBSTR(pText, '[{][0-9]+[}]',1,i),2, LENGTH(REGEXP_SUBSTR(pText, '[{][0-9]+[}]',1,i)) - 2)); 

     OPEN cuParameter(vPos); 
     FETCH cuParameter INTO vValue; 
     IF cuParameter%FOUND THEN 
      vText := REPLACE(vText, REGEXP_SUBSTR(pText, '[{][0-9]+[}]',1,i), vValue); 
     END IF; 
     CLOSE cuParameter; 
    END LOOP; 

    RETURN vText; 

EXCEPTION 
     WHEN OTHERS 
     THEN 
     RETURN pText; 
END FN_REPLACETEXT; 
/

Uso:

TEXT_RETURNED := FN_REPLACETEXT('Hello {0}, this is a {2} for {1}', TBL_VARCHAR2('world','all','message'));