Voici une solution qui utilise uniquement des fonctions de chaîne standard (plutôt que des expressions régulières) - ce qui devrait entraîner une exécution plus rapide dans la plupart des cas ; il supprime 3 uniquement lorsqu'il s'agit du premier caractère suivi d'une virgule, du dernier caractère précédé d'une virgule ou précédé et suivi d'une virgule, et il supprime la virgule qui le précède au milieu de la casse et il supprime la virgule qui le suit dans le premier et troisième cas.
Il est capable de supprimer deux 3 d'affilée (ce que certaines des autres solutions proposées ne sont pas capables de faire) tout en laissant en place des virgules consécutives (qui remplacent vraisemblablement NULL) et ne perturbent pas les nombres comme 38 ou 123.
La stratégie consiste d'abord à doubler chaque virgule (remplacer ,
avec ,,
) et ajouter et précéder une virgule (au début et à la fin de la chaîne). Supprimez ensuite chaque occurrence de ,3,
. De ce qui reste, remplacez chaque ,,
retour avec un seul ,
et enfin supprimer le ,
de début et de fin .
with
test_data ( str ) as (
select '1,2,3,4,5' from dual union all
select '1,2,3,3,4,4,5' from dual union all
select '12,34,5' from dual union all
select '1,,,3,3,3,4' from dual
)
select str,
trim(both ',' from
replace( replace(',' || replace(str, ',', ',,') || ',', ',3,'), ',,', ',')
) as new_str
from test_data
;
STR NEW_STR
------------- ----------
1,2,3,4,5 1,2,4,5
1,2,3,3,4,4,5 1,2,4,4,5
12,34,5 12,34,5
1,,,3,3,3,4 1,,,4
4 rows selected.
Remarque Comme l'a souligné MT0 (voir les commentaires ci-dessous), cela coupera trop si la chaîne d'origine commence ou se termine par des virgules. Pour couvrir ce cas, au lieu de tout envelopper dans trim(both ',' from ...)
Je devrais envelopper le reste dans une sous-requête et utiliser quelque chose comme substr(new_str, 2, length(new_str) - 2)
dans la requête externe.