2016-09-29 50 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。这可能是不可能实现的,但是有没有办法像百搭比赛那样,即如果自由文本'伊朗伊斯兰共和国'与国家名单相匹配,它将与'伊朗'具有更高的匹配百分比比'伊朗伊斯兰共和国'而不是'中华人民共和国',如果这是有道理的? –