2016-09-29 3 views
0

Я пытаюсь создать SQL UDF или инструкцию для анализа свободного текстового поля и поиска имени страны из него, но я не могу сделать это успешно.SQL-скрипт для извлечения страны из свободного текстового поля

Чтобы предоставить вам полный контекст, у меня есть таблица транзакций (tbltransactions ниже), которая содержит данные транзакции, и одно из полей - это свободное текстовое поле. Это должно быть ideal содержит имя получателя платежа, адрес получателя и страну получателя платежа (в указанном порядке). Но, как и следовало ожидать, в свободном текстовом поле есть всевозможные комбинации. Это также означает, что имя страны может быть неправильно записано, сокращено, сокращено или отсутствует полностью. К счастью, большинство транзакций имеют страны, указанные в конце текстового блока! В таблице есть другое поле, в котором пользователь вводит код страны с тремя символами (обязательно). Это может быть или не соответствовать тому, что он ввел в поле свободного текста. Ниже фиктивные данные в таблице:

TransID  ISOCode BeneAddress 
------------------- ----------- 
20   IRN  aaaa bb cccc Islamic Rupublic of Iran 
19   IRN  aaaa bb cccc Iran, Islamic Republic of 

Теперь, я сделал справочную таблицу (tblCountryMappings), который хранит список всех стран и возможные варианты их имен (а большинство из них!).

Например. 'Македония, Республика', 'Македония, бывшая югославская Республика OF', 'MACEDONIA', 'MASEDONIA' и т.д.

Ниже фиктивные данные для этой таблицы:

ID ISONumericCode countryName     matchIdentifier   matchIdentifierType 
---------------------------------------------------------------------------------------------- 
209 364   Iran, Islamic Republic of IR       ISOAlphaCode_2 
210 364   Iran, Islamic Republic of IRN      ISOAlphaCode_3 
495 364   Iran, Islamic Republic of Iran      Short_Name 
1163 364   Iran, Islamic Republic of Iran, Islamic Republic of Original_Name 
1309 364   Iran, Islamic Republic of Islamic Rupublic of Iran Alternate_Name 

Как вам видно, между таблицами существует сопоставление от одного до многих. Цель - это возможность проанализировать транзакцию и определить, в какой стране она была предназначена (в основном на основе поля свободного текста, а не только код ISO). Например, мы должны уметь видеть, что Transaction 123 имеет «Ирак» в коде ISO, а «Иран» - в свободном тексте, а свободное текстовое совпадение - «IRN» ISO 3-символа. Мне также необходимо убедиться, что сопоставление работает в граничных ситуациях (например, конец строки, окруженный кавычками), но не в том случае, если он находится в середине блока текста (например, не соответствует 2-символьному коду Саудовской Аравии «SA» кого-то называли «Самуил»).

Я написал этот базовый сценарий, чтобы извлечь последнее слово из свободного текста, и тогда его можно будет использовать для объединения matchIdentifier в tblCountryMappings, но это, безусловно, крайне неудачная попытка.

select 
    beneaddress 
    ,SUBSTRING(beneaddress, 
       case when CHARINDEX(' ',REVERSE(beneaddress)) = 0 then 1 
        else LEN(beneaddress) - CHARINDEX(' ',REVERSE(LTRIM(RTRIM(beneaddress))))+2 
       end 
    ,LEN(beneaddress)) as Country 
from 
    tblTransactions 

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

Большое спасибо.

Cheers

ответ

0

Я сомневаюсь, что это идеальное решение там, так как я могу представить себе странные сценарии, где отдельные названия улиц или государственные названия могут быть похожи на некоторые названия стран. Сказав, что вы можете присоединиться к своей таблице поиска в таблице транзакций, используйте оператор LIKE. Таким образом, вы можете использовать регулярные выражения для соответствия стране в адресе. Название страны будет либо в конце, либо ограничено в начале или в конце либо «,», либо пробелом. Это должно упростить ваш запрос, но, как я уже говорил, он не будет идеальным.

В следующем примере показано, как будет выглядеть запрос.

DECLARE @tbltransactions TABLE 
(
    TransID  INT 
    ,ISOCode  NVARCHAR(3) 
    ,BeneAddress NVARCHAR(100) 
) 

DECLARE @tblCountryMappings TABLE 
(
    ID     INT IDENTITY 
    ,CountryName  NVARCHAR(100) 
    ,MatchIdentifier NVARCHAR(100) 
) 

INSERT INTO @tbltransactions 
(
    TransID  
    ,ISOCode  
    ,BeneAddress 
) 
VALUES 
(1   ,'IRN'  ,'aaaa bb cccc Islamic Rupublic of Iran') , 
(2   ,'IRN'  ,'aaaa bb cccc "Iran", Islamic Republic of'), 
(3   ,'IRN'  ,'aaRSAbb cccc IRN'), 
(4   ,'IRN'  ,'aaaa bb cccc IR'), 
(5   ,'IRN'  ,'aaaa bb cccc The Country of Fred') 


INSERT INTO @tblCountryMappings 
(  
    CountryName  
    ,MatchIdentifier 
) 
VALUES 
('Iran, Islamic Republic of', 'IR'),   
('Iran, Islamic Republic of', 'IRN'),   
('Iran, Islamic Republic of', 'Iran'), 
('South Africa, Republic of', 'RSA'), 
('South Africa, Republic of', 'R.S.A.'), 
('South Africa, Republic of', 'South Africa') 


SELECT  T.TransID 
      ,T.BeneAddress 
      ,ISNULL(M.CountryName, '< Could not match country>') AS CountryName 
      ,M.MatchIdentifier 
FROM  @tbltransactions T 
LEFT OUTER JOIN @tblCountryMappings M ON 
       ( 
         (T.BeneAddress LIKE '%[, "]' + M.MatchIdentifier + '[, "]%') -- Match any address which contains a word that start with a comma or space or quote ([, "]) and is then followed by the MatchIdentifier and then end with either a comma or space or quote. 
           OR 
         (T.BeneAddress LIKE '%[, "]' + M.MatchIdentifier) -- Match any address which contains a word that start with a comma or space or quote ([, "]) and is then ends with the MatchIdentifier. 
           OR 
         (T.BeneAddress LIKE M.MatchIdentifier + '[, "]%') -- Match any address which contains a word that start with the MatchIdentifier and then end with either a comma or space or quote. 
           OR 
         (T.BeneAddress LIKE M.MatchIdentifier) -- Match the address with an exact match of the MatchIdentifier 
       ) 

В приведенном выше примере SQL будет соответствовать BeneAddress с регулярным выражением, формируемой на основании значения поля MatchIdentifier.

Пример в tblCountryMappings Поле MatchIdentifier будет иметь следующие значения для Ирана.

  • ИК
  • IRN
  • Иран

Это будет генерировать следующие регулярные выражения:

  • % [ "] ИК ["]% - Любая строка который содержит слово, начинающееся с запятой или пробелом или цитатой ([, "]), после чего следует IR, а затем заканчивается запятой или пробелом или цитатой.
  • % [, "] IRN [,"]% - сопоставить любую строку, содержащую слово, которое начинается с запятой или пробелом или цитатой ([, "]), а затем следует IRN, а затем заканчивается запятой или пространства или цитаты.
  • % [, "] Иран [,"]% - сопоставить любую строку, содержащую слово, начинающееся с запятой или пробелом или цитатой ([, "]), после чего следует Иран, а затем заканчивается либо запятой или пробел или цитаты.

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

+0

Спасибо за это решение @Edmond! Работает очень хорошо. Не могли бы вы рассказать мне, как вы присоединились к '(T.BeneAddress LIKE '% [,]' + M.MatchIdentifier + '[,]%')'. Мой sql немного слаб, поэтому не мог получить, как работает объединение без обычного A.x = B.x. Кроме того, тогда я пытаюсь включить дополнительные условия в регулярное выражение для обработки сценариев, таких как countryname в кавычках или в начале строки, или если freetext имеет только имя страны, ничего другого - в настоящее время они не возвращают совпадение. Другая проблема, с которой я столкнулся, заключается в том, что она возвращает совпадение с именами получателей, а также, например, «Mr bb Sudan» вернет Барбадос и Судан. Спасибо! –

+0

@ V.Asher само соединение действительно не сильно отличается от традиционного объединения, вместо того, чтобы выполнять точное совпадение между T.BeneAddres и M.MatchIdentifier, вместо этого он будет присоединяться к T.BeneAddres, когда он будет соответствовать шаблону, как определено регулярным выражение. Я обновлю ответ на пример о том, как обращаться с котировками и названиями стран в начале строки с дополнительным объяснением. К сожалению, вы не можете многое сделать по сценариям, где имя человека также может совпадать с именем графства, так как кабина названия страны находится в начале строки или конца. –

+0

Спасибо большое! Поэтому, если я правильно вас понял: '(T.BeneAddress LIKE '% [,]' + M.MatchIdentifier + '[,]%')' будет объединяться, только если оба поля содержат ** ',' ** и '(T .BeneAddress LIKE '% [,]' + M.MatchIdentifier) ​​'присоединятся, только если T.BeneAddress имеет его, а M.MatchIdentifier - нет? За последний бит, который вы упомянули, для ~ 90% записей, название страны в конце. Ура! –

0

Если я понимаю ваш вопрос.

Следующие возвращают САМОЕ САМОСТОЯТЕЛЬНОЕ КОЛИЧЕСТВО МАТЧИХ ГИТОВ.Это действительно требует функции PARSING перечисленных ниже:

Создание образца данных

Declare @YouTable table (TransID int,ISOCode varchar(50),BeneAddress varchar(500)) 
Insert Into @YouTable values 
(20,'IRN','aaaa bb cccc Islamic Rupublic of Iran'), 
(19,'IRN','aaaa bb cccc Iran, Islamic Republic of') 

Declare @ISO table (ID int,ISONumericCode int,countryName varchar(50),matchIdentifier varchar(50),matchIdentifierType varchar(50)) 
Insert Into @ISO values 
(209 ,364,'Iran, Islamic Republic of','IR',      'ISOAlphaCode_2'), 
(210 ,364,'Iran, Islamic Republic of','IRN',      'ISOAlphaCode_3'), 
(495 ,364,'Iran, Islamic Republic of','Iran',      'Short_Name'), 
(1163 ,364,'Iran, Islamic Republic of','Iran, Islamic Republic of','Original_Name'), 
(1309 ,364,'Iran, Islamic Republic of','Islamic Rupublic of Iran' ,'Alternate_Name') 

Actual SQL

;with cteBase as (
     Select A.*,B.*,C.* 
      From @YouTable A 
      Cross Apply (Select * from [dbo].[udf-Str-Parse](A.BeneAddress,' ') ) B 
      Cross Apply (Select * from @ISO where matchIdentifier like '%'+B.RetVal+'%') C),  
     cteSumm as (
     Select TransID,ID,RowNr=Row_Number() over (Partition By TransID Order by Count(*) Desc) 
     From cteBase 
     Group By TransID,ID 
) 
Select B.*,C.* 
From cteSumm A 
Join @YouTable B on (A.RowNr=1 and A.TransID = B.TransID) 
Join @ISO C  on (A.RowNr=1 and A.ID=C.ID) 

Возвраты

enter image description here

ОДС

CREATE FUNCTION [dbo].[udf-Str-Parse] (@String varchar(max),@Delimiter varchar(10)) 
Returns Table 
As 
Return ( 
    Select RetSeq = Row_Number() over (Order By (Select null)) 
      ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)'))) 
    From (Select x = Cast('<x>'+ Replace(@String,@Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A 
    Cross Apply x.nodes('x') AS B(i) 
); 
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',') 
--Select * from [dbo].[udf-Str-Parse]('John||Cappelletti||was||here','||') 
+0

Привет, Джон, спасибо за решение. Это работает очень хорошо.Единственная проблема заключается в том, что он пытается сопоставить каждое слово в BeneAddress, например, «of», «the» и т. Д., Например. «Mr.X People's Republic of China» найдет матч с «Ираном, Исламской Республикой» из-за соответствующего слова «of». –

+0

@ V.Asher Да, но он возвращает верхний удар. Я буду немного лапшой. –

+0

Cheers @John. Это может быть невыполнимо, но может ли быть способ иметь что-то вроде процентного совпадения, т. Е. Если свободный текст «Иран, Исламская Республика» будет соответствовать списку стран, он будет иметь более высокий процент матча с «Ираном», чем с «Ираном, Исламской Республикой», чем с «Китайской Народной Республикой», если это имеет смысл? –

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