Sql server 通过TSQL存储过程将多场景文本匹配到表

Sql server 通过TSQL存储过程将多场景文本匹配到表,sql-server,stored-procedures,Sql Server,Stored Procedures,我想看看在以下场景中是否有更好的方法来编写存储过程: 场景 我有一段文本,需要与person表中某个人的全名进行匹配,以获得他的唯一id 挑战 uniquepersonid FullName ------------------------------ 12345 John Doe 23456 Jane Doe 在尝试匹配各种模式时,我编写的函数已成为批处理中成本最高的查询(每个执行计划)。当我在一个包含文本的表的50万条记录上运行了几次,同时在包含1

我想看看在以下场景中是否有更好的方法来编写存储过程:

场景

我有一段文本,需要与person表中某个人的全名进行匹配,以获得他的唯一id

挑战

uniquepersonid  FullName
------------------------------
12345           John Doe
23456           Jane Doe
在尝试匹配各种模式时,我编写的函数已成为批处理中成本最高的查询(每个执行计划)。当我在一个包含文本的表的50万条记录上运行了几次,同时在包含100000多人的persons表上运行了几次

人员表(相关列)样本

uniquepersonid  FullName
------------------------------
12345           John Doe
23456           Jane Doe
文本可以采用以下任何格式(或更多格式):

  • 无名氏
  • 无名氏
  • 无名氏
  • 无名氏(成员)
  • 无名氏/
  • John Doe等人
  • 功能

        CREATE FUNCTION [dbo].[FN_Extract_Person] 
           (@text as nvarchar(1000))
        RETURNS int
        AS
        BEGIN
             declare @result int
    
             --direct comparison         
             select @result = uniquepersonid 
             from persons 
             where fullname = ltrim(rtrim(@text))
    
             if (@result > 0)
                 return @result
        end
        --eliminate / and .
        set @text = replace(@text,'/','')
        set @text = replace(@text,'.','')
    
        select @result = uniquepersonid 
        from persons 
        where fullname = ltrim(rtrim(@text))
    
        if (@result > 0)
             return @result
        end
    
        --eliminate comma
        set @text = replace(@text,',','')
    
        select @result = uniquepersonid 
        from persons 
        where fullname = ltrim(rtrim(@text))
    
        -- and so on for other patterns  
        return @result
    END
    

    为了获得更好的性能,我建议您在表中添加一个
    computed列
    ,然后将函数设置为formula,到目前为止,将计算每个记录的纯名称,但这不会对查询产生显著的改进。要解决这个问题,请将计算列上的IsPersisted属性设置为true,然后再次执行查询,您会感到惊讶。

    您已经为Fullname编制了索引,不是吗?假设你有,也许这种方法有帮助

    CREATE FUNCTION [dbo].[FN_Extract_Person] 
           (@text as nvarchar(1000))
    RETURNS int
    AS
    BEGIN
        declare @result INT
    
        WITH options AS(
            SELECT * FROM (VALUES
                  (1, ltrim(rtrim(@text)))
                , (2, ltrim(rtrim(replace(replace(@text,'/',''),'.',''))))
                , (3, ltrim(rtrim(replace(replace(replace(@text,'/',''),'.',''),',',''))))
                , (4, ltrim(rtrim(SUBSTRING(@text,1,PATINDEX('%[(.{]%', @text)))))
            ) AS T(Prio, Name)
        )
        SELECT TOP 1 @result = uniquepersonid, Prio FROM dbo.persons AS P
            INNER JOIN options AS O
                ON P.name = O.name
        ORDER BY Prio;
    
        if (@result > 0)
            return @result
    END
    
    编辑:使用标量函数可能会遇到更大的问题,因为它的内容取决于您使用该函数的方式。例如,使用这个函数查询是个坏主意。在这种情况下,您可能需要使用内联函数,如下所示:

    CREATE FUNCTION [dbo].[IF_Extract_Person] 
           (@text as nvarchar(1000))
    RETURNS TABLE RETURN
        WITH options AS(
            SELECT * FROM (VALUES
                  (1, ltrim(rtrim(@text)))
                , (2, ltrim(rtrim(replace(replace(@text,'/',''),'.',''))))
                , (3, ltrim(rtrim(replace(replace(replace(@text,'/',''),'.',''),',',''))))
                , (4, ltrim(rtrim(SUBSTRING(@text,1,PATINDEX('%[(.{]%', @text)))))
            ) AS T(Prio, Name)
        )
        SELECT TOP 1 uniquepersonid FROM dbo.persons AS P
            INNER JOIN options AS O
                ON P.name = O.name
        ORDER BY O.Prio
    GO
    

    EDIT2:进行了一些优化,可能会有所帮助。您可能需要提供更多的上下文,并告诉我们如何使用该函数

    计算列是个好主意。然而,在我的例子中,文本正在被解析,其中一个步骤是提取唯一id。例如:“John Doe,来自xyz社区学院”是一种模式,当拆分时,将得到John Doe,作为提取uniquepersonid的文本。文本可能包含人,也可能不包含人。您如何确定全名何时/何处结束?有两个姓氏没有连字符的人(例如“Paul van Dyk”或“Reginald Von Hoobie Doobie”)呢?重复的名字(如“约翰·史密斯”)如何?这里好像有点不对劲。无论如何,如果您不针对
    person
    进行3次选择,您的函数将更快。只需进行3次替换,然后进行一次选择。幸运的是,这是文本中遵循的一个模式,名称后面总是有一组模式(在我们的分析过程中已经确定)。只要名称与person表匹配(不管是一个姓还是两个姓),它对我们仍然有效。有些模式也很复杂,文本并非总是以人名开头。有趣的方法。不确定,但是这个函数与我的其他函数结合使用时需要更多的时间。我首先添加了6个标准。思想?