2016-10-26 5 views
4

私は「A B A A C D」と呼ばれるリストを持っています。私の期待する結果は「A B C D」です。これまでのウェブから私が見つけたのはOracle regexp_replaceによってスペース区切りリストから重複を削除するにはどうすればよいですか?

regexp_replace(l_user ,'([^,]+)(,[ ]*\1)+', '\1'); 

表現です。しかしこれは分けられたリストのためです。スペースで区切られたリストを作成するためには、変更を行う必要があります。注文を考慮する必要はありません。

答えて

4

私がよく分かっているのであれば、 '、'をスペースで置き換えるだけでなく、よりスマートな方法で重複を削除する必要があります。

私が代わりにスペースで動作するように、その式を変更する場合「」、私はあなたが必要なもの、'A B A C D'を与える

select regexp_replace('A B A A C D' ,'([^ ]+)([ ]*\1)+', '\1') from dual 

をしませ得ます。

あなたに必要な結果が複雑もう少し、以下のことができ取得する方法は:ここ

with string(s) as (select 'A B A A C D' from dual)  
    select listagg(case when rn = 1 then str end, ' ') within group (order by lev) 
    from (
      select str, row_number() over (partition by str order by 1) rn, lev 
      from (
       SELECT trim(regexp_substr(s, '[^ ]+', 1, level)) str, 
         level as lev 
        FROM string 
       CONNECT BY instr(s, ' ', 1, level - 1) > 0 
       ) 
     ) 

私の主な問題は、私は非隣接の重複をチェックする正規表現を構築することはできませんよということです、だから、私は文字列を分割し、重複をチェックし、重複していない値を再び集計して、順序を保つ必要があります。

あなたは結果の文字列内のトークンの順番を気にしない場合は、これを簡略化することができる。

with string(s) as (select 'A B A A C D' from dual) 
select listagg(str, ' ') within group (order by 1) 
from (
     SELECT distinct trim(regexp_substr(s, '[^ ]+', 1, level)) as str 
      FROM string 
     CONNECT BY instr(s, ' ', 1, level - 1) > 0 
    ) 
+1

長く待つ必要はありませんでした。 :-)しかし、唯一の改善は外側のクエリです - rn = 1の行だけを使用してください。これは、この場合に可能な唯一の単純化です。 – mathguy

+0

@mathguy - 私はあまりにも多くのネスティングを持っていた感覚を持っていた::)まだいくつかの正規表現の組み合わせが分割を避けることができますが、あまりにも多くの希望... – Aleksej

+0

長い選択ネストはヒットします。それでも私は正しい解決策を超えて処理時間を犠牲にすることができます。ありがとう –

4

は、あなたが彼らの最初の出現のために、コンポーネントの文字列を維持したいと仮定すると、(とありませんたとえば、アルファベット順に並べ替えます。この例では例が貧弱ですが、どちらも同じ結果につながるためです)、問題はより複雑になります。次に、各文字について、最初の出現を維持する必要があります。ここにはrow_number()が役立ちます。

with 
    inputs (str) as (select 'A B A A C D' from dual) 
-- end test data; solution begins below this line 
select listagg(token, ' ') within group (order by id) as new_str 
from (
     select level as id, regexp_substr(str, '[^ ]+', 1, level) as token, 
       row_number() over ( 
          partition by regexp_substr(str, '[^ ]+', 1, level) 
          order by level) as rn 

     from inputs 
     connect by regexp_substr(str, '[^ ]+', 1, level) is not null 
    ) 
where rn = 1 
; 
+0

注文は問題ではありません。 'A B A A C D'が変数であればここに渡します –

+0

どのような変数ですか? (または、テーブルの列の値を意味しますか?)それがバインド変数(たとえば ':str')の場合は、カラム名が' str'の場合はいつでも ':str'と書いてください。注 - テーブルでこれを行う必要があり、入力テーブルに複数の行がある場合、「接続」クエリは少し複雑になります。 – mathguy

+0

また、結果の文字列内のトークンの順序が最初の文字列に関連している必要がない場合、解決策はサブクエリ - 外部クエリ構造(および 'row_number()'の使用)を避けることができます。 – mathguy

3

Xquery?

select xmlquery('string-join(distinct-values(ora:tokenize(.," ")), " ")' passing 'A B A A C D' returning content) result from dual 
+0

この作業を行うために自分のシステムに必要なものは何ですか?私はマシン上で試してみましたが、出力は 'A B C D 'ではなく'(XMLTYPE) 'です。 – mathguy

+0

wrapp arround 'xmlcast(xmlquery(...)as varchar2(2000))' –

関連する問題