2010-06-01 6 views
20

J'utilise la fonction REPLACE dans oracle pour remplacer des valeurs dans ma chaîne comme;Fonction REPLACE multiple dans Oracle

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

c'est donc OK pour remplacer une valeur, mais qu'en est-20+, 20+ dois-je utiliser la fonction REPLACE ou est-il une solution plus pratique.

Toutes les idées sont les bienvenues.

Répondre

31

Même si ce fil est ancien est le premier sur Google, je vais donc poster un équivalent Oracle à la fonction implémentée ici, en utilisant des expressions régulières.

Est assez plus rapide que nested replace(), et beaucoup plus propre.

Pour remplacer les chaînes 'a', 'b', 'c' avec 'd' dans une colonne de chaîne à partir d'une table donnée

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

Il n'y a rien d'autre qu'une expression régulière pour plusieurs modèles statiques avec 'ou' opérateur.

Méfiez-vous des caractères spéciaux regexp!

+0

Cette solution simple est beaucoup mieux que d'écrire vos propres fonctions en faisant ce qui a déjà été mis en œuvre. – Tosz

+0

Je suis d'accord c'est la meilleure réponse en ce qui concerne la question réelle – dangel

+0

Beaucoup mieux! Merci beaucoup! – GeorgiG

20

La réponse acceptée à how to replace multiple strings together in Oracle suggère d'utiliser des instructions REPLACE imbriquées, et je ne pense pas qu'il existe une meilleure solution.

Si vous allez faire un usage intensif de cela, vous pourriez envisager d'écrire votre propre fonction:

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; 

puis l'utiliser comme ceci:

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 

Ce texte avec des jetons à remplacer

Si tous vos jetons ont le même format ('#VAL' || i || '#'), vous pouvez omettre le paramètre in_old et utiliser plutôt votre compteur de boucles.

+0

Merci Peter Lang – Adnan

+0

@Adnan: Vous êtes les bienvenus, mais j'aurais attendu pendant un certain temps avant d'accepter ma réponse - Il pourrait y avoir d'autres/meilleures réponses là-bas :) –

+0

'RETOUR VARCHAR2 DETERMINISTIC' pourrait être probablement plus rapide. – Benoit

16

Gardez à l'esprit les conséquences

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

remplacera le 123 456, puis trouver qu'il peut remplacer le 45 par 89. Pour une fonction qui a un résultat équivalent, il faudrait dupliquer le priorité (c'est-à-dire remplacer les chaînes dans le même ordre). De même, en prenant une chaîne 'ABCDEF', et en lui demandant de remplacer 'ABC' par '123' et 'CDE' par 'xyz', il faudrait toujours prendre en compte une préséance pour déterminer si elle est passée à '123EF' ou ABxyzF '. En bref, il serait difficile de trouver quelque chose de générique qui serait plus simple qu'un REPLACE imbriqué (bien que quelque chose qui était plus d'une fonction de style sprintf serait un ajout utile).

3

Ceci est un ancien article, mais j'ai fini par utiliser les pensées de Peter Lang, et j'ai fait une approche similaire, mais différente. Voici ce que je faisais:

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; 

Ensuite, je peux l'utiliser comme ceci:

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

Cela fait en sorte que je peux peut tout caractère/chaîne avec un caractère/chaîne.

J'ai écrit un post à ce sujet sur mon blog.

4

Dans le cas où toutes vos sources et les chaînes de remplacement ne sont qu'un caractère long, vous pouvez simplement utiliser la fonction TRANSLATE:

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

Voir the Oracle documentation pour plus de détails.

1

J'ai créé une fonction Oracle de chaîne multi-remplacement générale par une table de varchar2 en tant que paramètre. Le varchar sera remplacé pour la valeur rownum de la table.

Par exemple:

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

Retours:

Hello world, this is a message for all. 

Vous devez créer un type:

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

Le funcion est:

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; 
/

Utilisation:

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