web-dev-qa-db-fra.com

Fonction REPLACE multiple dans Oracle

J'utilise la fonction REPLACE dans Oracle pour remplacer les valeurs de ma chaîne comme;

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

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

Toutes les idées sont les bienvenues.

22
Adnan

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

Est plus rapide que replace () imbriqué et beaucoup plus propre.

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

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

Ce n'est rien d'autre qu'une expression régulière pour plusieurs modèles statiques avec l'opérateur 'ou'.

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

41
user3270011

Gardez à l'esprit les conséquences

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

remplacera le 123 par 456, puis trouvera qu'il peut remplacer le 45 par 89. Pour une fonction qui avait un résultat équivalent, il faudrait dupliquer la priorité (c'est-à-dire remplacer les chaînes dans le même ordre).

De même, prendre une chaîne 'ABCDEF' et lui demander de remplacer 'ABC' par '123' et 'CDE' par 'xyz' devra toujours tenir compte d'une priorité 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 REMPLACEMENT imbriqué (même si quelque chose qui était plus une fonction de style sprintf serait un ajout utile).

22
Gary Myers

La réponse acceptée à comment remplacer plusieurs chaînes ensemble dans Oracle suggère d'utiliser des instructions imbriquées REPLACE, et je ne pense pas qu'il existe une meilleure façon.

Si vous allez en faire un usage intensif, 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 utilisez-le 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

Ceci est du texte avec quelques jetons à remplacer

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

21
Peter Lang

C'est un vieux post, mais j'ai fini par utiliser les pensées de Peter Lang, et j'ai fait une approche similaire, mais pourtant différente. Voici ce que j'ai fait:

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 puisse utiliser n'importe quel caractère/chaîne avec n'importe quel caractère/chaîne.

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

4
TJ Abrahamsen

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

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

Voir la documentation Oracle pour plus de détails.

4
Frank Schmitt

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

Par exemple:

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

Résultats:

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

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

Usage:

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