如何比较SQL Server中格式不同的两个名称字符串?

时间:2010-11-16 17:01:21

标签: sql sql-server stored-procedures

比较SQL Server中以下字符串集的最佳方法是什么?

目的:此存储过程的主要目的是将一组客户输入名称与特定帐户的客户数据库中存在的名称进行比较。如果输入名称与数据库中的名称存在差异,则应触发使用新名称信息更新客户数据库。

条件:

输入格式:FirstName [MiddleName] LastName

数据库中的值格式:LastName, FirstName MiddleName

当出现这样的名字时会出现并发症,

实施例

输入:Dr. John A. Mc Donald

数据库:Mc Donald, Dr. John A.

对于由2个或更多部分组成的姓氏,必须采用什么逻辑 确保将输入中的姓氏与数据库中的姓氏进行比较,同样确保名字和中间名。

我已经考虑过将数据库值分解为临时HASH表,因为我知道数据库中','之前的所有内容都是姓。然后,我可以检查输入是否包含姓氏,并从中分出FirstName [MiddleName],以便对数据库执行与','之后的值的另一次比较。

然而,还有第二部分。如果输入名称具有完全新的姓氏(即,如果数据库中的名称是Mary Smith,但更新的输入名称现在是Mary Mc Donald)。在这种情况下,将','之前的姓氏的数据库值与输入名称进行比较将导致不匹配,这是正确的,但此时代码如何知道姓氏在输入值中的起始位置?怎么知道她的中间名不是MC而她的姓唐纳德?

有没有人不得不像以前那样处理类似的问题?你最终选择了哪些解决方案?

我非常感谢您的意见和建议。

谢谢。

3 个答案:

答案 0 :(得分:2)

实际上,知道像“Mary Jane Evelyn Scott”这样的名字是第一个中间的最后一个 - 最后一个2,第一个 - 第一个 - 中间 - 最后一个,第一个 - 第一个 - 最后一个 - 最后一个,这在计算上是非常困难的(如果不是不可能的话) ,或者其他一些组合......而且甚至没有进入文化考虑......

所以个人而言,我建议改变数据结构(相应地,应用程序的输入字段)。而不是名称的单个字符串,将其分成几个字段,例如:

FullName{
  title,      //i.e. Dr., Professor, etc.
  firstName,  //or given name
  middleName, //doesn't exist in all countries!
  lastName,   //or surname
  qualifiers  //i.e. Sr., Jr., fils, D.D.S., PE, Ph.D., etc.
}

然后用户可以选择他们的名字是“Mary”,他们的中间名是“Jane Evelyn”,他们的姓氏是“Scott”。

<强>更新
根据您的意见,如果您必须完全在SQL中执行此操作,我将执行以下操作:

  1. 为给定输入字符串“firstname [middlename] lastname”
  2. 的“lastname,firstname [middlename]”的所有可能组合构建一个表
  3. 根据原始数据的连接和所有可能的排序运行查询。
  4. 因此,第1步将采用字符串“Dr. John A. Mc Donald”并创建值表:

    'Donald, Dr. John A. Mc'
    'Mc Donald, Dr. John A.'
    'A. Mc Donald, Dr. John'
    'John A. Mc Donald, Dr.'
    

    然后步骤2.将搜索数据库中所有这些字符串的出现次数。

    假设MSSQL 2005或更高版本,步骤1.可以使用一些递归CTE来实现,并修改我用来拆分CSV字符串的方法(找到here)(SQL不是理想的语言对于这种形式的字符串操作......):

    declare @str varchar(200)
    set @str = 'Dr. John A. Mc Donald'
    
    --Create a numbers table
    select [Number] = identity(int)
    into #Numbers
    from sysobjects s1
        cross join sysobjects s2
    
    create unique clustered index Number_ind on #Numbers(Number) with IGNORE_DUP_KEY
    
    ;with nameParts as (
        --Split the name string at the spaces.
        select [ord] = row_number() over(order by Number),
            [part] = substring(fn1, Number, charindex(' ', fn1+' ', Number) - Number)
        from (select @str fn1) s
            join #Numbers n on substring(' '+fn1, Number, 1) = ' '
        where Number<=Len(fn1)+1
    
    ),
    lastNames as (
        --Build all possible lastName strings.
        select [firstOrd]=ord, [lastOrd]=ord, [lastName]=cast(part as varchar(max))
        from nameParts
        where ord!=1 --remove the case where the whole string is the last name
        UNION ALL
        select firstOrd, p.ord, l.lastName+' '+p.part
        from lastNames l
            join nameParts p on l.lastOrd+1=p.ord
    ),
    firstNames as (
        --Build all possible firstName strings.
        select [firstOrd]=ord, [lastOrd]=ord, [firstName]=cast(part as varchar(max))
        from nameParts
        where ord!=(select max(ord) from nameParts) --remove the case where the whole string is the first name
        UNION ALL
        select p.ord, f.lastOrd, p.part+' '+f.firstName
        from firstNames f
            join nameParts p on f.firstOrd-1 = p.ord
    )
    --Combine for all possible name strings.
    select ln.lastName+', '+fn.firstName
    from firstNames fn
        join lastNames ln on fn.lastOrd+1=ln.firstOrd
    where fn.firstOrd=1
        and ln.lastOrd = (select max(ord) from nameParts)
    
    drop table #Numbers
    

答案 1 :(得分:1)

由于我对来自第三方的数据有着可怕的经验,因此几乎可以保证输入数据将包含大量不符合指定格式的垃圾。
当您尝试匹配数据多部分字符串数据时,我会使用以下方法将输入和数据预处理为“规范化字符串”。

  1. 删除所有非ascii字符(保留特定于语言的字符“č”)
  2. 紧凑空格(用单个空格替换多个空格)
  3. 小写
  4. 分为单词
  5. 删除重复项
  6. 按字母顺序排序
  7. 加入以破折号分隔的字符串
  8. 使用样本数据,此函数将产生:

      

    博士。 John A. Mc Donald - &gt;   a-donald-dr-john-mc
    麦克唐纳德博士   John A .-&gt;一个-唐纳德-DR约翰-MC

    不幸的是,它不是100%防弹,有些情况下退化的输入会产生无效的匹配。

答案 2 :(得分:0)

您的姓名字段在数据库中不好。重新设计并摆脱它。如果您有名字,中间名,姓氏,前缀和后缀结构,则可以使用具有您正在使用的结构的hava计算字段。但这是一种非常差的存储数据的方式,您的首要任务应该是停止使用它。

既然你有一个共同的客户ID,为什么你不匹配而不是名字?