2016-12-20 18 views
0

文字列内の複数の値の最初のオカレンスを置き換えたいとします。動作しません|演算子を使用して,a,a,,b,cOracle - 複数の値の最初のオカレンスを置き換えます。

入力文字列:a,a,a,b,b,c

値を交換する:a,b

予想される出力Plsは、次の例を考えてみ以下に示すように、

select regexp_replace('a,a,a,b,b,c','a|b',null,1,1) from dual; 

出力:,a,a,b,b,c。これは、aの最初のオカレンスだけを置き換えます。 aが複数回置換される場合、これに拡張として

は、第2の発生も入力に置き換えられるべきである。

入力ストリング:なるようにa,a,a,b,b,c

値を置き換え:a,b,a

予想される出力:,,a,,b,c

これはREGEXP_REPLACEなどを使用して達成可能ですか?はいの場合、どうですか?あなたの助けを前もってありがとう!

+1

あなたは明確交換のルールを記述してくださいすることができます。私はなぜ2番目の例が 'a、b、a'に置き換えられるのか理解できません。 – Kacper

+0

@Kacper私が理解しているように、 'a、b、a 'は2つの最初の' A'と最初の 'B'値を置き換えます。 'a、b'は' A'の最初の値と 'B'の最初の値を置き換えます。 – sagi

+0

私はルールを理解していません。テキストは最初の複数の出現を置き換えますので、私はそれぞれの文字の出現を別々に扱うと、Ex1の結果が 'a、b、b、c'または' a、b、c 'であることを意味します。 – Kacper

答えて

-1

一つの解決策は、2つのregexp_replace() Sを使用することです:

select regexp_replace(regexp_replace('a,a,a,b,b,c', 'a', null, 1, 1), 'b', null, 1, 1) 
from dual; 

私は、単一の正規表現を使用して(合理的な)解決策があるかどうかはわかりません。

+0

これで一般的な解決策を得る方法はありません。非常に特別な場合は、regexp_replace( 'regexp_replace(input_string、input_string、output_string)を選択してください。 'はい、3つの引数すべてをハードコードします。あるいは、 'output_string'を選択する方が良いでしょう。この答えで提案されているものよりも悪いことはありません。 – mathguy

+0

@mathguy。 。 。あなたのコメントが何を言っているのか分かりません。この回答は、OPが求めている "a"と "b"の最初の出現を置き換えます。 –

+0

入力が 'x、x、y、y、z'だった場合、最初の' a'と最初の 'b'を取り除くべきでしょうか?彼は最初に「a」と「b」が出現するのではなく、「複数の値**が最初に出現する」と言った。 – mathguy

0

もっと良い解決策かもしれないと思います。

私は再帰的SQL =( は、下のスクリプトに見えるだけでこれを行うことができます:

with replaced_string as (
    select 'a,a,a,b,c,b,d,a,d' as str from dual union all 
    select 'a,a,a,b,c,d,a,d,d' as str from dual 
) 
,expr_for_replace as (
    select 'a,b,a,d' as pattern from dual 
) 
,recursive_call (str, lvl, parent_str) as (
    select t.str as str, 1 as lvl, t.str as parent_str 
    from replaced_string t 
    union all 
    select case 
      when regexp_instr(r.str, v.val, 1, 2) > 0 
      then regexp_replace(r.str,v.val,null,1,1) 
      else r.str 
      end str 
      ,r.lvl + 1 as lvl 
      ,r.parent_str as parent_str 
    from recursive_call r 
    left join (select regexp_substr(pattern, '[^,]+',1,level) as val 
        ,level as lvl 
       from expr_for_replace 
       connect by regexp_substr(pattern, '[^,]+',1,level) is not null) v on (v.lvl = r.lvl) 
    where v.lvl is not null 
) 

select parent_str as string_before 
     ,str  as result_string 
from (select c.* 
      ,row_number() over(partition by parent_str order by c.lvl desc) as rn 
     from recursive_call c) 
where rn = 1 
0

ここでは完全に一般的にこれを行うための一つの方法だ、私は問題を正しく理解した場合は、カンマで区切られた文字列を与えられました。後で文字列の中で繰り返される各シンボル(トークン)をNULLに置き換えますが、すべてのコンマをプレースホルダとして保持します。解決方法は、元の文字列に既にNULLのプレースホルダがある場合(つまり、トークンがない連続したコンマ極端な場合、元の文字列は空でもかまいません(OracleではNULLと同じです)。

問題の唯一の他の妥当な意味は、「シンボル」または「トークン」は、直ちに同じシンボルが続く場合にのみ削除することです。つまり、a,a,a,b,b,a,a,b,b,bのような入力は,,a,,b,,a,,,bになるはずです。最初の解釈では、それは,,,,,,a,,,bになるはずです。以下の解決策は、「最初の解釈」(aと修正された文字列に残っているのはbの1つだけです)を実装しています。 OPの質問は、どの解釈が望まれるかについては明確ではない。「第2の解釈」が望ましい場合は、以下の解決策を変更することができます(基本的な「Tabibitosanメソッド」を使用して同じ連続値を識別します)。

トークンはNULLのトークンが存在する可能性があるため注意深く分割し、各個別シンボルの最後のオカレンスを特定し、すべてのヌルトークンと「グループの最後」でないすべてのトークンをプレースホルダシンボルここでは「〜」を使用しました。入力文字列で使用されていない記号である必要があります)、すべてをLISTAGG()で集計し、すべてのプレースホルダ記号(~)を削除します。 LISTAGG()は入力からNULLを削除するため、プレースホルダが必要です(Oracle開発者が残念な選択です)。

解決方法は正規表現を使用します。コードをもっと複雑にするという代償を払って、パフォーマンスが非常に重要な場合は避けることができます(代わりにINSTRSUBSTRを使用してください)。

with 
-- begin test data definition (do not include in final query) 
    test_data as (
     select 1 as id, 'a,a,a,b,b,c' as str from dual union all 
     select 2  , ''     from dual union all 
     select 3  , 'a,b,a,b,b,a'  from dual union all 
     select 4  , 'x,y,z,z,z'   from dual union all 
     select 5  , 'a,a,a,a'   from dual union all 
     select 6  , 'a,b,c,c,a'   from dual union all 
     select 7  , 'x'     from dual union all 
     select 8  , 'p,q,r'    from dual union all 
     select 9  , 'a,b,,,a,c'   from dual 
    ), 
-- end of test data; solution (query) continues below this line 
    prep as (
     select id, str, level as lvl, 
       regexp_substr(str, '([^,]*)(,|$)', 1, level, null, 1) as token 
     from test_data 
     connect by level <= regexp_count(str, ',') + 1 
       and prior id = id 
       and prior sys_guid() is not null 
    ), 
    with_rn as (
     select id, str, lvl, token, 
       row_number() over (partition by id, token order by lvl desc) as rn 
     from prep    
    ) 
select id, str, 
     translate(listagg (case when token is null or rn > 1 then '~' 
           else token end, ',' 
         ) within group (order by lvl), 'x~', 'x' 
       ) as modif_str 
from with_rn 
group by id, str 
; 

出力

ID STR   MODIF_STR 
--- -------------- -------------- 
    1 a,a,a,b,b,c ,,a,,b,c 
    2 
    3 a,b,a,b,b,a ,,,,b,a 
    4 x,y,z,z,z  x,y,,,z 
    5 a,a,a,a  ,,,a 
    6 a,b,c,c,a  ,b,,c,a 
    7 x    x 
    8 p,q,r   p,q,r 
    9 a,b,,,a,c  ,b,,,a,c 

9 rows selected. 
関連する問題