2016-09-29 53 views
0

我試圖創建一個SQL UDF或語句來解析自由文本字段並從中找到國家名稱,但我無法成功完成此操作。SQL腳本從自由文本字段中提取國家

爲了給大家提供完整的上下文,我有一個交易表(下面的tbltransactions),其中包含交易細節,其中一個字段是這個自由文本字段。這應該是理想情況下包含收款人姓名,收款人地址和收款人國家(按此順序)。但正如你所期望的那樣,有一個免費的文本字段,有各種可能的組合。這也意味着一個國家的名稱可能會被拼寫錯誤,縮寫,縮短或完全丟失。幸運的是,大多數交易都在文本塊末尾指定了國家/地區!表中還有另外一個字段,用戶輸入3個字符的國家代碼(強制性)。這可能會或可能與他在自由文本字段中輸入的內容不匹配。下面是在表中的虛擬數據:

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

現在,我做了存儲所有國家的列表和它們的名稱可能變化的查找表(tblCountryMappings)(以及大部分的!)。

例如, '馬其頓共和國', '馬其頓,前南斯拉夫共和國 的', '馬其頓', '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代碼中有「伊拉克」,在自由文本中有「Iran」,自由文本匹配在ISO 3字符「IRN」上。我還需要確保在邊界情況下的匹配工作(例如行尾,用引號包圍),但不是在文本塊的中間(例如,不匹配沙特阿拉伯雙字符代碼「SA」到任何人稱爲「塞繆爾」)。

我已經寫了這個基本腳本來從自由文本中提取最後一個單詞,然後這可以用於在tblCountryMappings中加入matchIdentifier,但它顯然是一個非常糟糕的嘗試。

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 

如果你能幫助我建立這個解決方案,它將非常感激。如果我違反了發佈規則,請原諒我,因爲這是我第一次。隨時索取更多信息,我會盡快發佈。

非常感謝。

乾杯

回答

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字段對於伊朗將具有以下值。

  • IR
  • IRN
  • 伊朗

,這將產生以下的正則表達式:

  • %[ 「] IR [」]% - 匹配任何字符串其中包含以逗號或空格或引號([,「])開頭的單詞,然後是IR,然後以逗號或空格或引號結尾。
  • %[,「] IRN [,」]% - 匹配包含以逗號或空格或引號開頭的單詞的任何字符串([,「]),然後是IRN,然後以逗號或空間或報價。
  • %[,「] Iran [,」]% - 匹配包含以逗號,空格或引號開頭的單詞的任何字符串([,「]),然後由伊朗跟隨,然後以逗號或空間或引用。

要匹配的可能性,該國可能會在我們包括在沒有模式匹配爲最終定義一個附加OR條件字符串的結尾。類似匹配的可能性,該國名稱可能在字符串的開頭,我們包含額外的OR條件,其中沒有爲匹配的模式匹配。

+0

中感謝這個解決方案@Edmond!工作得很好。你能否告訴我你是如何加入'(T.BeneAddress LIKE'%[,]'+ M.MatchIdentifier +'[,]%')'。我的SQL有點弱,所以無法得到如何加入工作W/O通常A.x = B.x.另外,然後我試着在正則表達式中引入更多的條件來處理引用中的countryname或字符串開頭的情況,或者如果freetext只有國家名稱沒有其他的東西 - 因爲目前他們不會返回匹配。我遇到的另一個問題是,它還會針對收款人姓名進行比賽,例如「bb蘇丹先生」將返回巴巴多斯和蘇丹。謝謝! –

+0

@ V.Asher連接本身與傳統連接實際上沒什麼區別,除了在T.BeneAddres和M.MatchIdentifier之間執行完全匹配之外,它將在與常規定義的模式相匹配時加入T.BeneAddres表達。 我會用一個例子來更新答案,如何在字符串的開頭處理引號和國家名稱並附加一些解釋。不幸的是,由於國名駕駛室位於字符串的開頭或結尾,因此對於名稱可能與縣名相匹配的場景,您可以做的事情並不多。 –

+0

非常感謝!所以如果我正確理解了你的話:'(T.BeneAddress LIKE'%[,]'+ M.MatchIdentifier +'[,]%')'只會在兩個字段都包含**','**和'(T .BeneAddress LIKE'%[,]'+ M.MatchIdentifier)'只有在M.MatchIdentifier沒有時T.BeneAddress纔會加入?對於你提到的最後一點,對於大約90%的記錄,國家名稱是最後的。乾杯! –

0

如果我理解你的問題。

以下將返回最高數量的匹配匹配。它確實需要如下解析函數:

創建示例數據

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') 

實際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

的UDF

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'等。 'X中華人民共和國'將與'伊朗伊斯蘭共和國'匹配,因爲'匹配'字。 –

+0

@ V.Asher是的,但它正在返回頂端。我會多一點麪條。 –

+0

乾杯@John。這可能是不可能實現的,但是有沒有辦法像百搭比賽那樣,即如果自由文本'伊朗伊斯蘭共和國'與國家名單相匹配,它將與'伊朗'具有更高的匹配百分比比'伊朗伊斯蘭共和國'而不是'中華人民共和國',如果這是有道理的? –