Här är en lösning som bara använder vanliga strängfunktioner (snarare än reguljära uttryck) - vilket borde resultera i snabbare exekvering i de flesta fall; det tar bara bort 3 när det är det första tecknet följt av kommatecken, det sista tecknet som föregås av komma, eller föregås och följs av kommatecken, och det tar bort kommatecken som föregår det i mellanbokstaven och det tar bort kommatecken som följer det i första och tredje fallet.
Den kan ta bort två 3:or i rad (vilket några av de andra lösningarna som erbjuds inte kan göra) samtidigt som den lämnar kvar på varandra följande kommatecken (som förmodligen står för NULL) och stör inte siffror som 38 eller 123.
Strategin är att först dubbla upp varje kommatecken (ersätt ,
med ,,
) och lägg till och framför ett kommatecken (till början och slutet av strängen). Ta sedan bort varje förekomst av ,3,
. Från det som är kvar, byt ut varje ,,
tillbaka med en enda ,
och ta slutligen bort den inledande och efterföljande ,
.
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.
Obs Som påpekat av MT0 (se kommentarer nedan), kommer detta att trimma för mycket om den ursprungliga strängen börjar eller slutar med kommatecken. För att täcka det fallet, istället för att slå in allt inom trim(both ',' from ...)
Jag bör slå in resten i en underfråga och använda något som substr(new_str, 2, length(new_str) - 2)
i den yttre frågan.