-1

我想比较具有两个不同 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
5      PARACETAMOL, DEXTROMETHORPHAN, PSEUDOEPHEDRINE, PYRILAMINE

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
11      PARACETAMOL, DEXTROMETHORPHAN, PSEUDOEPHEDRINE, PYRILAMINE1

MY RESULT 应该只显示基于两个表中的 ALL NAME 匹配的匹配行。

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

下面现有成员@Goran 提供了部分解决方案,以下解决方案适用于除最后一行之外的所有值。下面的解决方案是找到 T1 的第 5 行与 T2 的第 11 行的匹配项,这是错误的,因为 T2 的最后一行值是“PYRILAMINE1”,它是 <> 到 T1 最后一行值“PYRILAMINE”

部分解决方案:

SELECT
    T1.ID_T1,
    T2.ID_T2,
    T1.NAME1
FROM
    T1
    JOIN T2 ON TRIM('#' FROM TRANSLATE(T1.NAME1, T2.NAME2, '#')) IS NULL
               AND TRANSLATE(T1.NAME1, T2.NAME2, '#') IS NOT NULL
               AND REGEXP_COUNT(T1.NAME1, ',') = REGEXP_COUNT(T2.NAME2, ',');
4

1 回答 1

0

这是一种方法,纯粹在 SQL 中,不利用 Oracle 12 及更高版本的任何功能(因为您没有告诉我们您的版本,所以我不想做出假设)。如果您发现自己不止一次需要这种比较,最好按照下面代码中显示的行编写一个“normalize_string”函数,并在需要时使用它。它会做什么(以及下面的代码做什么)是将逗号分隔的字符串拆分为标记,从每个标记的前面和结尾修剪空格,将每个标记转换为大写(允许输入小写字母),重复标记(允许相同的标记在输入中出现多次),然后重新创建逗号分隔的列表,标记按字母顺序出现。那么,这

更好的是,如果它在你的权力范围内(要么自己做,要么影响老板去做),那就是改变数据模型。您应该有一个单独的小表用于各个成分,并带有一个主键(可能是一个数字,当然不是成分的名称)以供其他表参考。然后你需要一个单独的药物小表,也有一个主键(不是药物名称!)最后是一个多对多关系表,每个药物中的每种成分都有一行 - 都由它们各自的键标识(不是名字)。这将避免药物或成分名称中的拼写错误、小写而不是大写等,这将使您的所有代码更容易编写、测试和维护,并且速度更快。你能做到吗?

= = = = = = =

在下面的输出中,我显示了它出现的“名称” t1(在您想要的输出中,您显示了它出现的名称,t2但我假设您不在乎)。也许最好显示标准化版本(norm1而不是name1)。

select n1.id_t1, n2.id_t2, n1.name1
from
  ( select id_t1, name1, listagg(token, ', ') within group (order by token) norm1
    from   ( select  distinct id_t1, name1,
                     upper(trim(substr(str, instr(str, ',', 1, level) + 1,
                           instr(str, ',', 1, level + 1) 
                              - instr(str, ',', 1, level) - 1))) token
             from    (select id_t1, name1, ',' || name1 || ',' as str from t1)
             connect by  level <= length(str) - length(replace(str, ',')) - 1 
                     and prior id_t1 = id_t1
                     and prior sys_guid() is not null
           )
    group  by id_t1, name1
  ) n1
  inner join
  ( select id_t2, name2, listagg(token, ', ') within group (order by token) norm2
    from   ( select  distinct id_t2, name2, 
                     upper(trim(substr(str, instr(str, ',', 1, level) + 1,
                           instr(str, ',', 1, level + 1)
                              - instr(str, ',', 1, level) - 1))) token
             from    (select id_t2, name2, ',' || name2 || ',' as str from t2)
             connect by  level <= length(str) - length(replace(str, ',')) - 1 
                     and prior id_t2 = id_t2
                     and prior sys_guid() is not null
           )
    group  by id_t2, name2
  ) n2
  on n1.norm1 = n2. norm2
;

输出

 ID_T1  ID_T2 NAME1                                                             
------ ------ ------------------------------------------------------------------
     1      4 ASCORBIC ACID, PARACETAMOL, POTASSIUM HYDROGEN CARBONATE          
     3      6 CAFFEINE, PARACETAMOL PH. EUR.                                    
     4     10 PSEUDOEPHEDRINE HYDROCHLORIDE,DEXCHLORPHENIRAMINE MALEATE 
于 2019-10-06T13:53:27.070 回答