从自由文本字段

时间:2016-09-29 16:34:08

标签: sql-server sql-server-2008 tsql parsing freetext

我试图创建一个SQL UDF或语句来解析自由文本字段并从中找到国家/地区名称,但我无法成功完成。

为了给大家提供完整的上下文,我有一个交易表(下面的tbltransactions),其中包含交易详情,其中一个字段是此自由文本字段。这应该理想包含收款人姓名,收款人地址和收款人国家(按此顺序)。但正如您对自由文本字段所期望的那样,有各种可能的组合。这也意味着一个国家/地区的名称可能会被错误拼写,缩写,缩短或完全丢失。幸运的是,大多数交易都在文本块的末尾指定了国家/地区!表格中还有另一个字段,用户输入3个字符的国家/地区代码(必填)。这可能与他在自由文本字段中输入的内容匹配也可能不匹配。下面是表中的虚拟数据:

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

现在,我已经制作了一个查找表(tblCountryMappings),它存储了所有国家/地区的列表以及它们名称的可能变体(大部分都是!)。

  

EG。 '马其顿,共和国,' MACEDONIA,前南斯拉夫共和国   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代码)。例如,我们应该能够看到,交易123在ISO代码中具有“伊拉克”,在自由文本中具有“伊朗”,并且自由文本匹配在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

如果你能帮助我建立这个解决方案,我们将非常感激。如果我违反任何发布规则,请原谅我,因为这是我第一次。请随时询问更多信息,我会尽早发布。

提前多多感谢。

干杯

2 个答案:

答案 0 :(得分: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')

实际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','||')

答案 1 :(得分: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字段将为Iran提供以下值。

  • IR
  • IRN
  • 伊朗

这将生成以下正则表达式:

  • %[,&#34;] IR [,&#34;]% - 匹配任何包含以逗号或空格或引号开头的单词的字符串([,&#34;]),然后按照通过IR,然后以逗号或空格或引号结束。
  • %[,&#34;] IRN [,&#34;]% - 匹配包含以逗号或空格或引号开头的单词的任何字符串([,&#34;])然后跟随通过IRN,然后以逗号或空格或引号结束。
  • %[,&#34;] Iran [,&#34;]% - 匹配任何包含以逗号或空格或引号开头的单词的字符串([,&#34;]),然后按照通过伊朗,然后用逗号,空格或引号结束。

为了匹配国家/地区可能位于字符串末尾的可能性,我们包含一个额外的OR条件,其中没有为结尾定义模式匹配。类似于匹配国家/地区名称可能位于字符串开头的可能性,我们包含一个额外的OR条件,其中没有为开始定义模式匹配。