Сравнение значений, разделенных запятыми, из двух столбцов двух разных таблиц

Я хочу сравнить значения двух столбцов (таблица различий) с разделенными запятыми значениями двух разных таблиц Oracle. Я хочу найти строки, которые соответствуют всем значениям (NAME1 все значения должны соответствовать NAME2 значениям).

Примечание. Значения, разделенные запятыми, расположены в другом порядке.

Пример:

T1:

ID_T1             NAME1
===================================


1      ASCORBIC ACID, PARACETAMOL, POTASSIUM HYDROGEN CARBONATE
2      SODIUM HYDROGEN CARBONATE, SODIUM CARBONATE ANHYDROUS, CITRIC ACID
3      CAFFEINE, PARACETAMOL PH. EUR.
4      PSEUDOEPHEDRINE HYDROCHLORIDE,DEXCHLORPHENIRAMINE MALEATE

T2:

ID_T2          NAME2
=================================

 4      POTASSIUM HYDROGEN CARBONATE, ASCORBIC ACID, PARACETAMOL
 5      SODIUM HYDROGEN CARBONATE, SODIUM CARBONATE ANHYDROUS
 6      PARACETAMOL PH. EUR.,CAFFEINE
 7      CODEINE PHOSPHATE, PARACETAMOL DC
 8      DEXCHLORPHENIRAMINE MALEATE, DEXTROMETHORPHAN HYDROBROMIDE 
10      DEXCHLORPHENIRAMINE MALEATE, PSEUDOEPHEDRINE HYDROCHLORIDE

МОЙ РЕЗУЛЬТАТ должен отображать только совпадающую строку на основе ВСЕХ совпадений ИМЕНИ в обеих таблицах.

    ID_T1    ID_T2    MATCHING NAME
    ==================================
    1            4    POTASSIUM HYDROGEN CARBONATE, ASCORBIC ACID, PARACETAMOL
    3            6    PARACETAMOL PH. EUR.,CAFFEINE
    4           10    PSEUDOEPHEDRINE HYDROCHLORIDE,DEXCHLORPHENIRAMINE MALEATE

Пробовал с REGEXP_SUBST, но не смог заставить его работать.

Я использовал приведенный ниже код для анализа значений:

SELECT REGEXP_SUBSTR (NAME1, '[^,]+', 1, ROWNUM)
            FROM T1
           CONNECT BY ROWNUM <= LENGTH (NAME1) - 
           LENGTH (REPLACE (NAME, ',')) + 1

person Pramod Kumar    schedule 04.10.2019    source источник
comment
Хранение произвольно разделенных строк в столбце базы данных - плохая практика. Он нарушает Первую нормальную форму. Это не утомительный академический момент: как вы понимаете, пытаться использовать SQL для работы с такими строками сложно.   -  person APC    schedule 04.10.2019
comment
Понятно, но разве мы не можем проанализировать значения и выполнить соединение с помощью REGEXP?   -  person Pramod Kumar    schedule 04.10.2019


Ответы (1)


Вы можете преобразовать таблицы в первую нормальную форму, а затем сравнить соединения, которые хранятся в каждой строке. Отправной точкой может быть:

{1} Обозначьте каждую строку и запишите токены в новую таблицу. Дайте каждому токену его исходный идентификатор плюс трехбуквенный префикс, указывающий, из какой таблицы был получен токен. {2} Сгруппируйте строки новой («нормализованной») таблицы по идентификатору и выполните LISTAGG (). Выполните самостоятельное присоединение и найдите соответствующие «группы токенов».

{1} Токенизация, создание таблицы как выбор (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 () таким образом, чтобы вы получали «ложные срабатывания», то есть вещества с идентификаторами 110, 210 и 211 будут «соответствовать». (Другими словами: я не думаю, что это подходящий инструмент для этой работы.)

DBFIDDLE здесь

(перейдите по ссылке, чтобы увидеть образцы таблиц и запросов).

person stefan    schedule 06.10.2019
comment
@ stefan- БОЛЬШОЕ СПАСИБО !!! Ваше первое решение по своей природе более надежное и масштабируемое. - person Pramod Kumar; 11.10.2019
comment
Пожалуйста! Вы могли бы принять мой ответ или хотя бы проголосовать за него? :-) - person stefan; 11.10.2019
comment
Извините, Стефан, я пытался развернуть решение в соответствии с моей моделью данных, и ваше решение подходит для этого. Я принял ваш ответ и тоже проголосовал за него. Спасибо за ваше время. - person Pramod Kumar; 13.10.2019
comment
Не о чем сожалеть! Я рад, что вы задали вопрос, который мне показался интересным. Удачи! - person stefan; 13.10.2019