sql >> データベース >  >> RDS >> Oracle

2つの異なるテーブルの2つの列からのコンマ区切り値の比較

    テーブルを最初の正規形にしてから、各行に格納されている化合物を比較できます。出発点は次のとおりです。

    {1}各行をトークン化し、トークンを新しいテーブルに書き込みます。各トークンに元のIDをプラス与えます トークンがどのテーブルから来たかを示す3文字のプレフィックス。{2}新しい(「正規化された」)テーブルの行をIDでグループ化し、LISTAGG()を実行します。自己結合を実行し、一致する「トークングループ」を見つけます。

    {1}トークン化して、テーブルをselect(CTAS)として作成

    create table tokens
    as 
    select
      ltrim(        -- ltrim() and rtrim() remove leading/trailing spaces (blanks)
        rtrim( 
          substr( N.wrapped
          , instr( N.wrapped, ',', 1, T.pos ) + 1
          , ( instr( N.wrapped, ',', 1, T.pos + 1 ) - instr( N.wrapped, ',', 1, T.pos ) ) - 1 
          ) 
        )
      ) token
    , N.id
    from (        
      select ',' || name1 || ',' as wrapped, 'T1_' || to_char( id_t1 ) as id from t1 -- names wrapped in commas, (table)_id
      union all
      select ',' || name2 || ',' , 'T2_' || to_char( id_t2 ) from t2  
    ) N join (  
      select level as pos   -- (max) possible position of char in an existing token
      from dual 
      connect by level <= (
        select greatest(    -- find the longest string ie max position (query T1 and T2) 
          ( select max( length( name1 ) ) from t1 )
        , ( select max( length( name2 ) ) from t2 )
        ) as pos
        from dual
      )  
    ) T
      on T.pos <= ( length( N.wrapped ) - length( replace( N.wrapped, ',') ) ) - 1 
    ;
    

    CONNECT BYを使用せずにトークン化するためのインスピレーションは、このSOの回答 から得られました。 。

    TOKENSテーブルの内容は次のようになります。

    SQL> select * from tokens ;
    TOKEN                           ID       
    ASCORBIC ACID                   T1_1     
    SODIUM HYDROGEN CARBONATE       T1_2     
    CAFFEINE                        T1_3     
    PSEUDOEPHEDRINE HYDROCHLORIDE   T1_4     
    PARACETAMOL                     T1_100   
    sodium hydroxide                T1_110   
    POTASSIUM HYDROGEN CARBONATE    T2_4     
    SODIUM HYDROGEN CARBONATE       T2_5     
    PARACETAMOL PH. EUR.            T2_6     
    CODEINE PHOSPHATE               T2_7     
    DEXCHLORPHENIRAMINE MALEATE     T2_8     
    DEXCHLORPHENIRAMINE MALEATE     T2_10    
    PARACETAMOL                     T2_200 
    ...
    

    {2} GROUP BY、LISTAGG、自己参加

    select
      S1.id id1
    , S2.id id2
    , S1.tokengroup_T1
    , S2.tokengroup_T2
    from 
    (
      select substr( id, 4, length( id ) - 3 ) id
      , listagg( token, ' + ' ) within group ( order by token ) tokengroup_T1
      from tokens
      group by id 
      having substr( id, 1, 3 ) = 'T1_'
    ) S1 
      join 
    (
      select substr( id, 4, length( id ) - 3 ) id
      , listagg( token, ' + ' ) within group ( order by token ) tokengroup_T2
      from tokens
      group by id 
      having substr( id, 1, 3 ) = 'T2_'
    ) S2 
      on S1.tokengroup_T1 = S2.tokengroup_T2
    ;
    
    -- result
    ID1   ID2   TOKENGROUP_T1                                                 TOKENGROUP_T2                                                 
    4     10    DEXCHLORPHENIRAMINE MALEATE + PSEUDOEPHEDRINE HYDROCHLORIDE   DEXCHLORPHENIRAMINE MALEATE + PSEUDOEPHEDRINE HYDROCHLORIDE   
    110   210   potassium carbonate + sodium hydroxide                        potassium carbonate + sodium hydroxide                        
    1     4     ASCORBIC ACID + PARACETAMOL + POTASSIUM HYDROGEN CARBONATE    ASCORBIC ACID + PARACETAMOL + POTASSIUM HYDROGEN CARBONATE    
    3     6     CAFFEINE + PARACETAMOL PH. EUR.                               CAFFEINE + PARACETAMOL PH. EUR. 
    

    このようにすると、物質を(アルファベット順で)並べ替えることができます。また、ここで好きな「区切り文字」(「+」を使用)を選択することもできます。

    代替

    それがすべて役に立たない場合、またはこれが複雑すぎると思われる場合は、TRANSLATE()を使用してみてください。この場合、データセットからすべてのスペース/空白を削除することをお勧めします(クエリ内-ではありません 元のデータを変更する!)次のように:

    クエリ

    select 
      id1, id2
    , name1, name2
    from (
      select 
        id_t1 id1
      , id_t2 id2
      , T1.name1 name1
      , T2.name2 name2
      from T1
        join T2 
          on  translate( replace( T1.name1, ' ', '' ), replace( T2.name2, ' ', '' ), '!' )
            = translate( replace( T2.name2, ' ', '' ), replace( T1.name1, ' ', '' ), '!' )
    ) ;
    

    結果

      ID1   ID2 NAME1                                                                NAME2                                                        
        2     5 SODIUM HYDROGEN CARBONATE, SODIUM CARBONATE ANHYDROUS, CITRIC ACID   SODIUM HYDROGEN CARBONATE, SODIUM CARBONATE ANHYDROUS        
        3     6 CAFFEINE, PARACETAMOL PH. EUR.                                       PARACETAMOL PH. EUR.,CAFFEINE                                
      100    10 PARACETAMOL, DEXTROMETHORPHAN, PSEUDOEPHEDRINE, PYRILAMINE           DEXCHLORPHENIRAMINE MALEATE, PSEUDOEPHEDRINE HYDROCHLORIDE   
      110   210 sodium hydroxide, potassium carbonate                                sodium hydroxide, potassium carbonate
    

    注: サンプルデータに次の行を追加しました:

    -- T1
    110, 'sodium hydroxide, potassium carbonate'
    
    -- T2
    210, 'sodium hydroxide, potassium carbonate' 
    211, 'potassium hydroxide, sodium carbonate'
    

    「誤検知」を与える方法でTRANSLATE()を使用するのは簡単であることがわかりました。つまり、ID 110、210、および211の物質は「一致」しているように見えます。 (言い換えれば、これはこの仕事に適したツールではないと思います。)

    DBFIDDLEはこちら

    (リンクをたどって、サンプルのテーブルとクエリを確認してください。)




    1. MySQLの行を削除するにはクエリを削除します

    2. LIMIT句でbindValueメソッドを適用するにはどうすればよいですか?

    3. SQLiteデータベース列から数値だけを返す2つの方法

    4. sqliteに記録するデータを挿入しています