Oracle - 查找两个表之间的最佳匹配

时间:2016-07-01 23:26:46

标签: oracle

我和我的团队很想知道我们可以匹配两组不同数据的最佳方式。没有可以加入的密钥,因为这些数据来自两个彼此不了解的独立来源。我们将这些数据导入两个oracle表中,一旦完成,我们就可以开始寻找匹配。

两个表都包含一个完整的属性列表(如在不动产中)。我们需要将Table1中的Properties与Table2中找到的任何可能的匹配属性进行匹配。对于Table1中的每个记录,搜索Table2中的潜在匹配并确定匹配的概率。我和我的团队已经决定,最好的方法是比较两个表中每个表的地址字段。

一个问题是Table1以解析格式提供地址,并将地址编号,地址Street和甚至Address_type分配到单独的列中,而Table2仅包含一个用于保存地址的列。每个表都有City,State和Zip列,可以单独进行比较。

例如 - 见下表1和表2:

请注意,下面伪表中的主键是Key1和Key2,与它们所在的表匹配。

    +---------------+---------------+---------------+---------------+---------------+-------+-------+
    +               +    TABLE1     +               +               +               +       +       +
    +---------------+---------------+---------------+---------------+---------------+-------+-------+
    | Key1          | Addr_Number   | Addr_Street   | Addr_Type     | City          | State | Zip   |
    +---------------+---------------+---------------+---------------+---------------+-------+-------+
    | 1001          | 148           | Panas         | Road          | Robinson      | CA    | 76050 |
    | 1005          | 110           | 48th          | Street        | San Juan      | NJ    | 8691  |
    | 1009          | 8571          | Commerce      | Loop          | Vallejo       | UT    | 83651 |
    | 1059          | 714           | Nettleton     | Avenue        | Vista         | TX    | 29671 |
    | 1185          | 1587          | Orchard       | Drive         | Albuquerque   | PA    | 77338 |
    +---------------+---------------+---------------+---------------+---------------+-------+-------+


    +---------------+----------------------+---------------+---------------+---------------+
    +               +    TABLE2            +               +               +               +
    +---------------+----------------------+---------------+---------------+---------------+
    | Key2          | Address              | City          | State         | Zip           |
    +---------------+----------------------+---------------+---------------+---------------+
    | Ax89f         | 148 Panas Road       | Robinson      | CA            | 76050         |
    | B184a         | 110 48th Street      | San Juan      | NJ            | 08691         |
    | B99ff         | 8571 Commerce Lp     | Vallejo       | UT            | 83651         |
    | D81bc         | 714 Nettleton Ave    | Vista         | TX            | 29671         |
    | F84a2         | 1587 Orachard Dr     | Albuquerqu    | PA            | 77338         |
    +---------------+----------------------+---------------+---------------+---------------+

这里的目标是向用户提供一个输出,它只显示Table1中的所有记录和Table2中找到的最高匹配记录。当然可以找到许多可能是匹配的记录,但我们希望保持这种一对一的关系,而不是在这个初始输出中产生重复。输出应该只是表1中的一个记录,与表2中的最佳结果相匹配。

请参阅下面我尝试创建的所需输出的示例:

    +--------+-------+----------------+---------------------------+
    +        +       + Matched_Output +                           +
    +--------+-------+----------------+---------------------------+
    | Key1   | Key2  |  Percent_Match | num_Matched_Records > 90% |
    +--------+-------+----------------+---------------------------+
    | 1001   | Ax89f |  100%          |  5                        |   --All Parsed Values Match
    | 1005   | B184a |  98%           |  4                        |   --Zip Code prefixed with Zero in Table 2
    | 1009   | B99ff |  95%           |  3                        |   --Loop Vs Lp
    | 1059   | D81bc |  95%           |  2                        |   --Avenue Vs Ave
    | 1185   | F84a2 |  97%           |  2                        |   --City Spelled Wrong in Table 2 and Drive vs Dr
    +--------+-------+----------------+---------------------------+

在输出中,我希望看到Table1中的Key1和它旁边的匹配记录,表明它与Table2中的记录匹配到Key2。接下来我们需要知道这两个记录的匹配程度。表2中可能有许多记录显示匹配表1中记录的概率。实际上,表2中的每个记录都可以从0%到100%匹配一直分配一个百分比。

  

现在主要问题是:   如何获得这个百分比?

     

如何解析Table2中的Address列,以便我可以比较构成Table1中地址的每个列,然后对每个已解析的值应用比较算法?

到目前为止,这是我的团队和我自己提出的(头脑风暴,Spitballin,无论你想称之为什么)。

我们已经看了几个内置的Oracle函数来获取我们正在寻找的百分比以及尝试使用正则表达式。如果我可以点击谷歌并获得他们的一些搜索算法,我会。显然,我没有那么奢侈,必须自己设计。

regexp_count(table2_city,'(^| )'||REPLACE(table1_city,' ','|')||'($| )') city_score,
regexp_count(table2_city,'(^| )') city_max,

to_char((city_score/city_max)*100, '999G999G999G999G990D00')||'%' city_perc,

以上就是我和我的团队用作概念证明的内容。我们只是从两个表格中选择了这些值并运行了“regexp_count”'针对那些列的功能。以下是我们已经看过的一些其他功能:

SOUNDEX

REGEXP_LIKE

REGEXP_REPLACE

这些功能很棒,但我不确定它们是否可以在两个表之间的单个查询中使用以产生所需的输出。

另一个想法是我们可以创建一个Function(),它将我们想要用来比较的Address字段作为参数。然后,该函数将在Table2中搜索最高可能的匹配,并将Table2中的Key2值返回给用户。

  

功能(Addr_Number,Addr_Street,Addr_type,City,State)RETURN table2.key2

例如,也许这样的事情可以'工作:

Select tb1.key1, table2Function(tb1.Addr_Number, tb1.Addr_Street, tb1.Addr_type, tb1.City, tb1.State) As Key2
From Table1 tb1;

最后,只知道表1中目前有大约15k记录,表2中有20k记录。同样......表1中的每条记录都需要根据表2中的每条记录进行检查,以确定是否存在匹配。

我全都耳朵。并提前感谢您的反馈。

2 个答案:

答案 0 :(得分:3)

使用UTL_MATCH包:

Oracle安装程序

CREATE TABLE Table1 ( Key1, Addr_Number, Addr_Street, Addr_Type, City, State, Zip ) AS
SELECT 1001, 148,  'Panas',     'Road',   'Robinson',    'CA', 76050 FROM DUAL UNION ALL
SELECT 1005, 110,  '48th',      'Street', 'San Juan',    'NJ',  8691 FROM DUAL UNION ALL
SELECT 1009, 8571, 'Commerce',  'Loop',   'Vallejo',     'UT', 83651 FROM DUAL UNION ALL
SELECT 1059, 714,  'Nettleton', 'Avenue', 'Vista',       'TX', 29671 FROM DUAL UNION ALL
SELECT 1185, 1587, 'Orchard',   'Drive',  'Albuquerque', 'PA', 77338 FROM DUAL;


CREATE TABLE Table2 ( Key2, Address, City, State, Zip ) AS
SELECT 'Ax89f', '148 Panas Road',    'Robinson',   'CA', '76050' FROM DUAL UNION ALL
SELECT 'B184a', '110 48th Street',   'San Juan',   'NJ', '08691' FROM DUAL UNION ALL
SELECT 'B99ff', '8571 Commerce Lp',  'Vallejo',    'UT', '83651' FROM DUAL UNION ALL
SELECT 'D81bc', '714 Nettleton Ave', 'Vista',      'TX', '29671' FROM DUAL UNION ALL
SELECT 'F84a2', '1587 Orachard Dr',  'Albuquerqu', 'PA', '77338' FROM DUAL;

<强>查询

SELECT Key1,
       Key2,
       UTL_MATCH.EDIT_DISTANCE_SIMILARITY(
         A.Addr_Number || ' ' || A.Addr_Street || ' ' || A.Addr_Type
           || ' ' || A.City || ' ' || A.State || ' ' || A.Zip,
         B.Address || ' ' || B.City || ' ' || B.State || ' ' || B.Zip
       ) AS Percent_Match,
       CASE WHEN UTL_MATCH.EDIT_DISTANCE_SIMILARITY(
              A.Addr_Number || ' ' || A.Addr_Street || ' ' || A.Addr_Type,
              B.Address
            ) >= 90
            THEN 1
            ELSE 0
            END
       +
       CASE WHEN UTL_MATCH.EDIT_DISTANCE_SIMILARITY( A.City, B.City ) >= 90
            THEN 1
            ELSE 0
            END
       +
       CASE WHEN UTL_MATCH.EDIT_DISTANCE_SIMILARITY( A.State, B.State ) >= 90
            THEN 1
            ELSE 0
            END
       +
       CASE WHEN UTL_MATCH.EDIT_DISTANCE_SIMILARITY( A.Zip, B.Zip ) >= 90
            THEN 1
            ELSE 0
            END AS Num_Matched
FROM   Table1 A
       INNER JOIN
       Table2 B
       ON ( SYS.UTL_MATCH.EDIT_DISTANCE_SIMILARITY(
              A.Addr_Number || ' ' || A.Addr_Street || ' ' || A.Addr_Type
                || ' ' || A.City || ' ' || A.State || ' ' || A.Zip,
              B.Address || ' ' || B.City || ' ' || B.State || ' ' || B.Zip
            ) > 80 );

<强>输出

      KEY1 KEY2  PERCENT_MATCH NUM_MATCHED
---------- ----- ------------- -----------
      1001 Ax89f           100           4 
      1005 B184a            97           3 
      1009 B99ff            95           3 
      1059 D81bc            92           3 
      1185 F84a2            88           3 

答案 1 :(得分:0)

一些想法。

首先,您可能需要查看utl_match包: https://docs.oracle.com/cd/E18283_01/appdev.112/e16760/u_match.htm

然后:你肯定希望先通过邮政编码和状态进行匹配。也许在需要的地方添加前导零到邮政编码 - 虽然显然你的一个问题是拼写错误,而不仅仅是输入数据的不同包装。如果邮政编码中存在拼写错误,你可以或多或少地处理这个问题,但是如果状态中的拼写错误真的很糟糕。

您可能希望按城市对相似性进行评分,但通常不会有所帮助。例如,出于所有实际目的,纽约州布鲁克林区应该被视为与纽约纽约市相匹配,但是在您的项目中,您无法做到这一点。所以我会非常重视城市的匹配。

关于地址类型的类似评论;也许你可以创建一个具有等价的小表,例如Street,Str,Str。或Lane,Ln,Ln。但事实是,当他们给你一个地址时,人们往往不一致;他们可能会说&#34; Clover Street&#34;到一个来源和&#34; Clover Avenue&#34;到另一个。因此,最好只比较街道号码和街道名称。

祝你好运!

相关问题