【问题标题】:String search with any sequence任何序列的字符串搜索
【发布时间】:2018-11-13 08:00:24
【问题描述】:

我有以下两张表:

表 1:

CREATE TABLE tbl_str_match_1
(
    enumber int,
    ename varchar(100),
    eaddress varchar(500)
);

INSERT INTO tbl_str_match_1 VALUES(1,'John Mak','Hno 12 Street Road, USA');
INSERT INTO tbl_str_match_1 VALUES(2,'Shai Lee','UK');
INSERT INTO tbl_str_match_1 VALUES(3,'Smith Watson','Street X01 UAE');
INSERT INTO tbl_str_match_1 VALUES(4,'Ray Gibbs','SA 124');

表2:

CREATE TABLE tbl_str_match_4
(
    name varchar(100),
    [address] varchar(500)
);

INSERT INTO tbl_str_match_4 VALUES('Mak John','Street Road, Hno 12, USA');
INSERT INTO tbl_str_match_4 VALUES('Shai A Lee','UK');
INSERT INTO tbl_str_match_4 VALUES('A watson Smeeth ','UAE Street X01');
INSERT INTO tbl_str_match_1 VALUES('Henry Jay','RUS OP124');

我想使用传递的数字从表 tbl_str_match_1 中搜索名称,然后使用名称作为输入进行下一次搜索,然后从另一个名为 tbl_str_match_4 的表中找到名称和地址。

注意

  1. 姓名可以按任何顺序排列,例如第一个中间名或中间名或姓氏中间名,任何概率都是可能的。

  2. 我想从第二个表中找到名称和地址,并增加一列,即字符串的百分比匹配。

  3. 将进行两次搜索,第一次在表 tbl_str_match_1 上获取姓名,第二次在表 tbl_str_match_4 上获取姓名和地址。

  4. 对于第一条记录John Mak,它应该显示与Mak John 100% 匹配。

  5. 对于第二条记录,Shai Lee 应该显示 90% 与 Shai A Lee 匹配,因为出现了 A 中间名。

  6. 最后一条记录 Ray Gibbs 不会显示在结果集中,因为它与其他表值不匹配。

--查询:

WITH CTE1 AS
(   
    SELECT ename FROM tbl_str_match_1 WHERE enumber = 1
)
SELECT name,[address] FROM tbl_str_match_4 WHERE name LIKE '%'+(SELECT ename from CTE1)+'%'

预期结果

场景1:如果我通过enumber = 1,那么结果应该是:

    Name        Address                     Matching Percentage
    ------------------------------------------------------------
    Mak John    Street Road, Hno 12, USA    100

场景2:如果我通过enumber = 2,那么结果应该是:

    Name        Address                     Matching Percentage
    ------------------------------------------------------------
    Shai A Lee  UK                          90

场景 3:如果我通过 enumber = 3 那么结果应该是:

    Name                Address             Matching Percentage
    ------------------------------------------------------------
    A watson Smeeth     UAE Street X01      70

场景 4:如果我通过 enumber = 4 那么结果应该是:

没有结果,因为我们没有任何相关匹配。

    Name        Address                     Matching Percentage
    ------------------------------------------------------------

【问题讨论】:

  • levenshtein 距离???
  • 很高兴您正确发布了示例数据,但您也应该发布预期结果。
  • @ZoharPeled,添加了预期结果。

标签: sql-server sql-server-2008-r2


【解决方案1】:

希望以下内容有所帮助。

我首先将 tbl_1 和 tbl_4 名称中的名称标记为

之后,我将 tbl_1 中的标记与 tbl_4 进行比较

关于匹配百分比的问题。 在“Shai A Lee”的示例中,您有 2 个匹配项(“Shai”,“Lee”),总共 3 个(“Shai”,“A”,“Lee”),所以匹配百分比不应该是 66.67 吗?

with split_ename_1 
  as (
        SELECT a.enumber
            ,a.ename
            ,a.eaddress      
            ,split.a.value('.', 'VARCHAR(100)') AS Data  
        FROM  
        (
            SELECT enumber
                ,ename
                ,eaddress
                ,CAST ('<M>' + REPLACE(rtrim(ename), ' ', '</M><M>') + '</M>' AS XML) AS Data  
            FROM  tbl_str_match_1
        ) AS A CROSS APPLY Data.nodes ('/M') AS Split(a)
     )
,split_ename_4
   as (SELECT a.name            
             ,a.address      
             ,split.a.value('.', 'VARCHAR(100)') AS Data  
             ,COUNT(*) over(partition by a.name) as  tot_cnt
        FROM  
        (
            SELECT name
                   ,address
                   ,CAST ('<M>' + REPLACE(rtrim(name), ' ', '</M><M>') + '</M>' AS XML) AS Data  
              FROM  tbl_str_match_4
        ) AS A CROSS APPLY data.nodes ('/M') AS split(a)
       )
   select a.ename
         ,count(a.data) as tokens_1
         ,count(b.data) as tokens_4
         ,max(b.tot_cnt) as tot_tokens_4
         ,case when count(b.data)=0 then 0 else count(b.data)*1.00/max(b.tot_cnt)*1.00 end as matching_percentage
     from split_ename_1 a
left join split_ename_4 b
       on a.data=b.data
group by a.ename

【讨论】:

  • 工作正常,需要一点性能,因为 100 万条记录需要 23 秒,搜索列有索引。
  • 目前,查询会在飞行中拆分令牌。如果值是静态的,那么可能值得创建一个新表并使用令牌加载它。例如:使用 split_ename_1 和 split_ename_4 的内容创建表,并在列数据上创建索引。
【解决方案2】:

您可以使用CTE 结合STRING SPLIT 来完成这项工作

我在 tbl_str_match_4 中添加了一个 Identity 列以简化此操作

 DECLARE @enumber INT = 2

;WITH c1 AS 
( 
  --To split the ename from first  table 

   SELECT s.value AS name
   FROM tbl_str_match_1 t
   CROSS APPLY STRING_SPLIT(t.ename, ' ') AS s
   WHERE enumber=@enumber
)
,c2 AS
( 
   --To split the matching names from second table of matched records

   SELECT t.id,s.value AS name 
   FROM tbl_str_match_4 t
   CROSS APPLY STRING_SPLIT(t.name, ' ') AS s
   WHERE EXISTS(SELECT 1 FROM c1 c WHERE t.name LIKE '%'+c.name+'%')
)
,c3 AS 
( 
   --To calculate the percentage of match

   SELECT id,
   CAST (COUNT(c1.name) AS FLOAT )/ CAST (COUNT(c2.name) AS FLOAT ) * 100 As Percentage
   FROM c2
   LEFT JOIN  c1 on c1.name =c2.name
   GROUP BY id
) 
--display the details
SELECT t.*,c3.Percentage FROM tbl_str_match_4 t
JOIN c3 ON t.Id=c3.Id

DEMO

【讨论】:

    【解决方案3】:

    希望对你有帮助。

    with CTE1 as
    
    (
    Select enumber,Ltrim(SubString(ename,1,Isnull(Nullif(CHARINDEX(' ',ename),0),1000))) As Firstename,
    
    Ltrim(SUBSTRING(ename,CharIndex(' ',ename),
    CAse When (CHARINDEX(' ',ename,CHARINDEX(' ',ename)+1)-CHARINDEX(' ',ename))<=0 then 0 
    else CHARINDEX(' ',ename,CHARINDEX(' ',ename)+1)-CHARINDEX(' ',ename) end )) as Middleename,
    
    Ltrim(SUBSTRING(ename,Isnull(Nullif(CHARINDEX(' ',ename,Charindex(' ',ename)+1),0),CHARINDEX(' ',ename)),
    Case when Charindex(' ',ename)=0 then 0 else LEN(ename) end)) as Lastename
    
    From tbl_str_match_1
    ),
    
    CTE2 as
    
    (
    Select *,Ltrim(SubString(name,1,Isnull(Nullif(CHARINDEX(' ',name),0),1000))) As FirstName,
    
    Ltrim(SUBSTRING(name,CharIndex(' ',name),
    CAse When (CHARINDEX(' ',name,CHARINDEX(' ',name)+1)-CHARINDEX(' ',name))<=0 then 0 
    else CHARINDEX(' ',name,CHARINDEX(' ',name)+1)-CHARINDEX(' ',name) end )) as MiddleName,
    
    Ltrim(SUBSTRING(name,Isnull(Nullif(CHARINDEX(' ',name,Charindex(' ',name)+1),0),CHARINDEX(' ',name)),
    Case when Charindex(' ',name)=0 then 0 else LEN(name) end)) as LastName
    
    From tbl_str_match_4
    )
    
    select CTE2.name,CTE2.address from CTE1 inner join CTE2 on  CTE1.Firstename = CTE2.FirstName and CTE1.Lastename = CTE2.LastName
    where CTE1.enumber = 1
    

    【讨论】:

    • 无法获取第一条记录enumber = 1的结果,请查看添加的预期结果。
    • 你的意思是名字、中间名、姓氏不按顺序排列吗?
    • 是的。并且有可能出现名称拼写错误,例如一个表有 Santhana 和另一个 Santana,我也想通过显示字符串百分比匹配来显示这些记录,如记录 3 的预期结果所示。
    猜你喜欢
    • 1970-01-01
    • 2019-08-26
    • 2013-07-24
    • 1970-01-01
    • 2017-04-15
    • 1970-01-01
    • 2015-02-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多