2016-07-01 4 views
1

Моя команда и я заинтересованы в определении наилучшего способа сопоставления двух разных наборов данных. Нет никаких ключей, к которым можно присоединиться, поскольку эти данные поступают из двух отдельных источников, которые ничего не знают друг о друге. Мы импортируем эти данные в две таблицы оракула, и как только это будет сделано, мы можем начать искать совпадения.Oracle - найти лучшее совпадение между двумя таблицами

Обе таблицы содержат полный список свойств (как в недвижимости). Нам нужно сопоставить свойства в таблице 1 с любыми потенциальными соответствиями, найденными в таблице2. Для каждой записи в таблице 1 найдите таблицу2 для потенциального совпадения и определите вероятность совпадения. Моя команда и я решили, что лучший способ сделать это - сравнить поля Address с каждой из двух таблиц.

Единственный улов в том, что таблица 1 предоставляет адрес в формате Parsed и выделяет номер адреса, адрес Street и даже Address_type в отдельные столбцы, тогда как в таблице 2 содержится только один столбец для хранения адреса. Каждая таблица имеет столбцы City, State и Zip, которые можно сравнить отдельно.

Для примера - смотрите ниже Table1 и Table2:

Обратите внимание, что первичные ключи в моих таблицах псевдослучайных ниже являются 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   | 
    +---------------+----------------------+---------------+---------------+---------------+ 

Целью здесь является. предоставить пользователю выход, который просто отображает ВСЕ записи из Таблицы 1 и самую высокую согласованную запись, найденную в Таблице 2. Разумеется, может быть найдено много записей, которые могут быть потенциальным совпадением, но мы хотим сохранить это отношение один к одному и не производить Duplicates в этом исходном выпуске. Вывод должен быть только одним записям из таблицы, сопоставленным с лучшим поиском в таблице 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 
    +--------+-------+----------------+---------------------------+ 

В выходе я хочу видеть Key1 из Table1 и согласованной записи прямо рядом с ним, показывая, что он совпадает с записью в таблице 2 - Key2. Затем нам нужно знать, насколько хорошо эти две записи совпадают. В таблице 2 может быть много записей, которые показывают вероятность сопоставления записей в таблице 1. Фактически каждой отдельной записи в таблице 2 может быть присвоен процент от 0% до 100% соответствия.

Итак, теперь на главный вопрос: Как получить этот процент?

Как проанализировать столбец «Адрес» в таблице2, чтобы я мог сравнивать каждый из отдельных столбцов, которые составляют адрес в таблице1, а затем применить алгоритм сравнения для каждого анализируемого значения?

Пока это моя команда и я придумали (Мозговой штурм, Spitballin, все, что вы хотите назвать).

Мы рассмотрели пару встроенных функций Oracle, чтобы получить проценты, которые мы ищем, а также пытаемся использовать регулярные выражения. Если бы я мог попасть в Google и получить некоторые из своих алгоритмов поиска, я бы это сделал. Очевидно, у меня нет такой роскоши, и я должен ее создать.

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

Эти функции являются большими, но я не уверен, что они могут быть использованы в одном Запрос между обеими таблицами для получения желаемого результата.

Другая идея состоит в том, что мы могли бы создать функцию(), которая в качестве своих параметров принимает поля «Адрес», которые мы хотим использовать для сравнения. Затем эта функция будет искать таблицу 2 для наивысшего вероятного соответствия и вернуть пользователю значение Key2 из таблицы2.

Функция (Addr_Number, Addr_Street, Addr_type, город, штат) ВОЗВРАТ 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 и 20k в таблице 2 имеется примерно 15 тыс. записей. Снова ... каждая запись в Таблице 1 должна быть проверена против каждой записи в Таблице 2 для потенциального соответствия.

Я все уши. И заблаговременно за ваши отзывы.

ответ

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 
+0

Как я и предложил в своем ответе, может иметь смысл взвесить отдельные баллы в конечном счете - дать (очень?) Большой вес в состояние и почтовый индекс и (относительно) более низкий балл в город по причинам Я дал там. – mathguy

+0

Это абсолютно идеально. Я не вернусь к работе до вторника, но я хотел проверить ваше решение, как только мог, я развернул свою базу данных Oracle Express дома и выполнил ваш запрос. Оно прекрасно. Соединение справа от функции sys.utl_match действительно круто. Спасибо за советы, и я с нетерпением жду изучения этого пакета. –

0

Несколько мыслей.

Во-первых, вы можете взглянуть на пакет utl_match: https://docs.oracle.com/cd/E18283_01/appdev.112/e16760/u_match.htm

Тогда вы, конечно, хотите, чтобы соответствовать по почтовому индексу и состояния первой. Возможно, добавление основных нулей в ZIP-код там, где это необходимо - хотя, по-видимому, одна из ваших проблем - это опечатки, а не только разная упаковка входных данных. Если в почтовом коде есть опечатки, вы можете более или менее справиться с этим, но если есть опечатки в состоянии, которое действительно отстойно.

Возможно, вы захотите забить сходство по городу, но часто это не поможет. Например, для всех практических целей Бруклин, штат Нью-Йорк, следует рассматривать как подходящий Нью-Йорк, Нью-Йорк, но вы не можете сделать это в своем проекте. Поэтому я бы поставил очень низкий вес на сопоставление по городу.

Подобный комментарий о типе адреса; возможно, вы можете создать небольшую таблицу с эквивалентами, например Street, Str, Str. или Lane, Ln, Ln. Но часто люди не согласуются, когда они дают вам адрес; они могут сказать «Clover Street» в один источник и «Clover Avenue» другому. Поэтому вам может быть лучше сравнивать только номер улицы и название улицы.

Удачи вам!

+0

Нужно иметь возможность получить город/штат из ZIP, нет? – DCookie

+0

Город: Абсолютно нет. Я привел пример; Почтовый индекс 11201 является либо «Бруклин», либо «Нью-Йорк Сити», и, возможно, даже в официальных адресах каждый из них составляет не менее 25% от общего числа. Для государства, да; но вы предлагаете им добавить таблицу почтовых индексов и состояний для проверки их данных? Кроме того, помните, что OP также обеспокоен опечатками - и могут быть опечатки в почтовых кодах. – mathguy

+0

Бруклин не город, это город Нью-Йорка. – DCookie

Смежные вопросы