【问题标题】:Oracle - Find Best Match between Two tablesOracle - 查找两个表之间的最佳匹配
【发布时间】:2016-07-01 23:26:45
【问题描述】:

我和我的团队很想确定我们可以匹配两组不同数据的最佳方式。没有可以连接的键,因为这些数据来自两个彼此一无所知的独立来源。我们将这些数据导入到两个 oracle 表中,一旦完成,我们就可以开始寻找匹配项。

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

一个问题是 Table1 以 Parsed 格式提供地址,并将地址编号、地址 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% 匹配。

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

如何解析表 2 中的地址列,以便比较构成表 1 中地址的每一列,然后对每个解析值应用比较算法?

到目前为止,这是我和我的团队提出的(头脑风暴、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”函数。以下是我们查看的其他一些函数:

声音

REGEXP_LIKE

REGEXP_REPLACE

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

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

Function(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;

最后,只知道Table1中当前大约有15k条记录,Table2中有20k条记录。再次... 表 1 中的每条记录都需要与表 2 中的每条记录进行核对以寻找潜在的匹配项。

我全神贯注。并提前感谢您的反馈。

【问题讨论】:

    标签: oracle


    【解决方案1】:

    使用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 
    

    【讨论】:

    • 正如我在回答中建议的那样,在最终分数中权衡各个分数可能更有意义 - 对州和邮政编码给予(非常?)高权重,(相对)较低得分到城市,因为我在那里给出的原因。
    • 这绝对是完美的。我要到周二才能回去工作,但我想尽快查看您的解决方案,所以我在家中启动了我的 Oracle Express DB 并运行了您的查询。真漂亮。加入 sys.utl_match 函数真的很酷。感谢您的提示,我期待着探索这个包。
    【解决方案2】:

    一些想法。

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

    然后:您肯定会希望首先通过邮政编码和州进行匹配。也许在需要的地方添加前导零到邮政编码 - 尽管显然您的担忧之一是拼写错误,而不仅仅是输入数据的不同包装。如果邮政编码中有错别字,您或多或少可以处理,但如果该州有错别字,那真的很糟糕。

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

    关于地址类型的类似注释;也许您可以创建一个具有等价物的小表,例如 Street、Str、Str。或车道,Ln,Ln。但事实往往是人们在给你地址时并不一致。他们可能会对一个来源说“Clover Street”,而对另一个来源说“Clover Avenue”。因此,您最好只比较街道号码和街道名称。

    祝你好运!

    【讨论】:

    • 应该可以从 ZIP 获取城市/州,不是吗?
    • 城市:绝对不是。我举了一个例子;邮政编码 11201 要么是“布鲁克林”,要么是“纽约市”,甚至在官方地址中,这两者也可能至少占总数的 25%。对于国家,是的;但是您是否建议他们应该添加一个邮政编码和州表来验证他们的数据?此外,请记住,OP 也关心拼写错误 - 邮政编码中可能存在拼写错误。
    • 布鲁克林不是一个城市,它是纽约市的一个自治市。
    • @DCookie 这不是重点,不是吗?重要的是人们在地址中输入了什么。我刚刚用谷歌搜索了布鲁克林植物园(首先想到的是 - 抱歉,我不是来自那里)。查看他们自己在网页上提供的地址(在主页左侧的入口下方)。 bbg.org
    • 从技术上讲,如果您的邮政编码正确,您可以离开城市并填写地址。诚然,邮政编码存在拼写错误,我不知道输入数据对 OP 来说有多糟糕,但您当然可以从邮政编码开始。可能使用城市/州对数据进行第二次传递。
    猜你喜欢
    • 1970-01-01
    • 2014-02-24
    • 2015-05-23
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    相关资源
    最近更新 更多