SQL 从自由文本字段中提取国家/地区的脚本
SQL script to extract country from free text field
我正在尝试创建 SQL UDF 或语句来解析自由文本字段并从中找到国家/地区名称,但我无法成功。
为了给你们提供完整的上下文,我有一个交易 table(下面的 tbltransactions),其中包含交易详细信息,其中一个字段是这个自由文本字段。这应该 理想地 包含收款人姓名、收款人地址和收款人国家(按此顺序)。但正如您对自由文本字段所期望的那样,存在各种可能的组合。这也意味着一个国家的名称可能会被拼写错误、缩写、缩短或完全遗漏。幸运的是,大多数交易都在文本块的末尾指定了国家! table 中还有另一个字段,用户可以在其中输入 3 个字符的国家/地区代码(必填)。这可能与他在自由文本字段中输入的内容匹配,也可能不匹配。下面是 table:
中的虚拟数据
TransID ISOCode BeneAddress
------------------- -----------
20 IRN aaaa bb cccc Islamic Rupublic of Iran
19 IRN aaaa bb cccc Iran, Islamic Republic of
现在,我查看了 table (tblCountryMappings),它存储了所有国家的列表及其名称的可能变体(其中大部分!)。
Eg. 'Macedonia, Republic of', 'MACEDONIA,THE FORMER YUGOSLAV REPUBLIC
OF', 'MACEDONIA', 'MASEDONIA' etc.
下面是这个table的虚拟数据:
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
如您所见,table 之间存在一对多映射。 objective 是为了能够分析交易并找到它打算用于哪个国家(主要 基于自由文本字段,而不是只是 ISO 代码)。例如,我们应该能够看到事务 123 在 ISO 代码中有“Iraq”,在自由文本中有“Iran”,自由文本匹配在 ISO 3 字符“IRN”上。我还需要确保匹配在边界情况下有效(例如行尾,用引号括起来),但如果它在文本块的中间则不行(例如不匹配沙特阿拉伯的 2 字符代码“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
如果您能帮我构建这个解决方案,我们将不胜感激。如果我违反了 posting 的任何规则,请原谅我,因为这是我第一次。欢迎随时询问更多信息,我会尽快 post。
非常感谢。
干杯
如果我理解你的问题。
以下将 return 匹配的最高数量。它确实需要下面列出的 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)
Returns
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','||')
我怀疑是否存在完美的解决方案,因为我可以想象个别街道名称或州名称可能与某些国家/地区名称相似的奇怪场景。话虽如此,您可以将查找 table 加入事务 table 使用 LIKE
语句。这样您就可以使用正则表达式来匹配地址中的国家/地区。国家/地区名称将在末尾或在前面或末尾用“,”或 space 分隔。它应该会稍微简化您的查询,但正如我提到的那样,它并不完美。
以下示例显示查询的外观。
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 将使用根据 MatchIdentifier 字段的值生成的正则表达式匹配 BeneAddress。
tblCountryMappings 中的示例 MatchIdentifier 字段将具有以下伊朗值。
- IR
- IRN
- 伊朗
这将生成以下正则表达式:
- %[ "]IR[ "]% - 匹配任何包含以逗号或 space 或引号 ([ "]) 开头,然后是 IR 和然后以逗号或 space 或引号结尾。
- %[ "]IRN[ "]% - 匹配任何包含以逗号或 space 或引号 ([ "]) 开头,然后是 IRN 和然后以逗号或 space 或引号结尾。
- %[ "]Iran[ "]% - 匹配包含以逗号或 space 或引号 ([ "]) 开头,然后是 Iran 和然后以逗号或 space 或引号结尾。
为了匹配国家可能位于字符串末尾的可能性,我们添加了一个额外的 OR
条件,其中没有为末尾定义模式匹配。类似于匹配国家名称可能位于字符串开头的可能性,我们包含一个额外的 OR
条件,其中没有为开头定义模式匹配。
我正在尝试创建 SQL UDF 或语句来解析自由文本字段并从中找到国家/地区名称,但我无法成功。
为了给你们提供完整的上下文,我有一个交易 table(下面的 tbltransactions),其中包含交易详细信息,其中一个字段是这个自由文本字段。这应该 理想地 包含收款人姓名、收款人地址和收款人国家(按此顺序)。但正如您对自由文本字段所期望的那样,存在各种可能的组合。这也意味着一个国家的名称可能会被拼写错误、缩写、缩短或完全遗漏。幸运的是,大多数交易都在文本块的末尾指定了国家! table 中还有另一个字段,用户可以在其中输入 3 个字符的国家/地区代码(必填)。这可能与他在自由文本字段中输入的内容匹配,也可能不匹配。下面是 table:
中的虚拟数据TransID ISOCode BeneAddress
------------------- -----------
20 IRN aaaa bb cccc Islamic Rupublic of Iran
19 IRN aaaa bb cccc Iran, Islamic Republic of
现在,我查看了 table (tblCountryMappings),它存储了所有国家的列表及其名称的可能变体(其中大部分!)。
Eg. 'Macedonia, Republic of', 'MACEDONIA,THE FORMER YUGOSLAV REPUBLIC OF', 'MACEDONIA', 'MASEDONIA' etc.
下面是这个table的虚拟数据:
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
如您所见,table 之间存在一对多映射。 objective 是为了能够分析交易并找到它打算用于哪个国家(主要 基于自由文本字段,而不是只是 ISO 代码)。例如,我们应该能够看到事务 123 在 ISO 代码中有“Iraq”,在自由文本中有“Iran”,自由文本匹配在 ISO 3 字符“IRN”上。我还需要确保匹配在边界情况下有效(例如行尾,用引号括起来),但如果它在文本块的中间则不行(例如不匹配沙特阿拉伯的 2 字符代码“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
如果您能帮我构建这个解决方案,我们将不胜感激。如果我违反了 posting 的任何规则,请原谅我,因为这是我第一次。欢迎随时询问更多信息,我会尽快 post。
非常感谢。
干杯
如果我理解你的问题。
以下将 return 匹配的最高数量。它确实需要下面列出的 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)
Returns
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','||')
我怀疑是否存在完美的解决方案,因为我可以想象个别街道名称或州名称可能与某些国家/地区名称相似的奇怪场景。话虽如此,您可以将查找 table 加入事务 table 使用 LIKE
语句。这样您就可以使用正则表达式来匹配地址中的国家/地区。国家/地区名称将在末尾或在前面或末尾用“,”或 space 分隔。它应该会稍微简化您的查询,但正如我提到的那样,它并不完美。
以下示例显示查询的外观。
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 将使用根据 MatchIdentifier 字段的值生成的正则表达式匹配 BeneAddress。
tblCountryMappings 中的示例 MatchIdentifier 字段将具有以下伊朗值。
- IR
- IRN
- 伊朗
这将生成以下正则表达式:
- %[ "]IR[ "]% - 匹配任何包含以逗号或 space 或引号 ([ "]) 开头,然后是 IR 和然后以逗号或 space 或引号结尾。
- %[ "]IRN[ "]% - 匹配任何包含以逗号或 space 或引号 ([ "]) 开头,然后是 IRN 和然后以逗号或 space 或引号结尾。
- %[ "]Iran[ "]% - 匹配包含以逗号或 space 或引号 ([ "]) 开头,然后是 Iran 和然后以逗号或 space 或引号结尾。
为了匹配国家可能位于字符串末尾的可能性,我们添加了一个额外的 OR
条件,其中没有为末尾定义模式匹配。类似于匹配国家名称可能位于字符串开头的可能性,我们包含一个额外的 OR
条件,其中没有为开头定义模式匹配。