SQL:从全名字段解析第一个、中间个和最后一个名称

SQL:从全名字段解析第一个、中间个和最后一个名称,sql,sql-server,tsql,parsing,Sql,Sql Server,Tsql,Parsing,如何使用SQL从全名字段中解析出第一个、中间个和最后一个名称 我需要尝试匹配那些与全名不直接匹配的名字。我希望能够采取全名字段,并将其分为第一名,中间名和姓氏 数据不包括任何前缀或后缀。中间名是可选的。数据的格式为“第一个中间最后一个” 我对一些实用的解决方案感兴趣,这些解决方案可以让我在90%的过程中达到目标。如前所述,这是一个复杂的问题,因此我将单独处理特殊情况。除非您有非常、非常好的数据,否则这是一个非常重要的挑战。一种简单的方法是在空白处标记,并假设三个标记的结果是[first,midd

如何使用SQL从全名字段中解析出第一个、中间个和最后一个名称

我需要尝试匹配那些与全名不直接匹配的名字。我希望能够采取全名字段,并将其分为第一名,中间名和姓氏

数据不包括任何前缀或后缀。中间名是可选的。数据的格式为“第一个中间最后一个”


我对一些实用的解决方案感兴趣,这些解决方案可以让我在90%的过程中达到目标。如前所述,这是一个复杂的问题,因此我将单独处理特殊情况。

除非您有非常、非常好的数据,否则这是一个非常重要的挑战。一种简单的方法是在空白处标记,并假设三个标记的结果是[first,middle,last],两个标记的结果是[first,last],但您必须处理多个单词的姓氏(例如“Van Buren”)和多个中间名。

您确定完整的法定名称始终包括first、middle和last吗?我知道有些人只有一个法定全名,老实说,我不知道这是他们的名字还是姓。:-)我还认识一些人,他们的法定名称中有不止一个首名,但没有中间名。有些人有多个中间名

还有名字的顺序,在完整的法定名称中。据我所知,在一些亚洲文化中,姓氏在法律全称中排在第一位

更实用的是,您可以将全名拆分为空白,并将第一个标记作为名字,最后一个标记(或只有一个名称的情况下的唯一标记)作为姓氏。尽管这假设顺序总是相同的

  • 获取一个sql正则表达式函数。样本:
  • 使用正则表达式提取名称
    我推荐Expresso用于学习/构建/测试正则表达式

    不知道“全名”的格式很难回答

    它可以是“姓、名、中名”或“名、中名、姓”等

    基本上,您必须使用子字符串函数

    SUBSTRING ( expression , start , length )
    
    可能还有CHARINDEX函数

    CHARINDEX (substr, expression)
    
    计算要提取的每个零件的起点和长度

    假设格式为“First Name Last Name”,您可以(未测试..但应该接近):


    扭转这个问题,添加列来保存各个片段,并将它们组合起来以获得完整的名称

    这将是最佳答案的原因是,没有确定的方法可以确定一个人的名字是注册的,中间名是什么

    例如,您将如何拆分此项

    Jan Olav Olsen Heggelien
    
    这虽然是虚构的,但在挪威是一个法定名称,可以但不必这样拆分:

    First name: Jan Olav
    Middle name: Olsen
    Last name: Heggelien
    
    First name: Jan Olav
    Last name: Olsen Heggelien
    
    First name: Jan
    Middle name: Olav
    Last name: Olsen Heggelien
    
    SELECT 
      SUBSTRING(fullname, '(\\w+)') as firstname,
      SUBSTRING(fullname, '\\w+\\s(\\w+)\\s\\w+') as middle,
      COALESCE(SUBSTRING(fullname, '\\w+\\s\\w+\\s(\\w+)'), SUBSTRING(fullname, '\\w+\\s(\\w+)')) as lastname
    FROM 
    public.person
    
    或者,像这样:

    First name: Jan Olav
    Middle name: Olsen
    Last name: Heggelien
    
    First name: Jan Olav
    Last name: Olsen Heggelien
    
    First name: Jan
    Middle name: Olav
    Last name: Olsen Heggelien
    
    SELECT 
      SUBSTRING(fullname, '(\\w+)') as firstname,
      SUBSTRING(fullname, '\\w+\\s(\\w+)\\s\\w+') as middle,
      COALESCE(SUBSTRING(fullname, '\\w+\\s\\w+\\s(\\w+)'), SUBSTRING(fullname, '\\w+\\s(\\w+)')) as lastname
    FROM 
    public.person
    
    或者,像这样:

    First name: Jan Olav
    Middle name: Olsen
    Last name: Heggelien
    
    First name: Jan Olav
    Last name: Olsen Heggelien
    
    First name: Jan
    Middle name: Olav
    Last name: Olsen Heggelien
    
    SELECT 
      SUBSTRING(fullname, '(\\w+)') as firstname,
      SUBSTRING(fullname, '\\w+\\s(\\w+)\\s\\w+') as middle,
      COALESCE(SUBSTRING(fullname, '\\w+\\s\\w+\\s(\\w+)'), SUBSTRING(fullname, '\\w+\\s(\\w+)')) as lastname
    FROM 
    public.person
    
    我想在大多数语言中都可以找到类似的情况


    因此,与其试图解释没有足够信息的数据,不如存储正确的解释,并结合起来获得完整名称。

    我不确定SQL server,但在postgres中,您可以这样做:

    First name: Jan Olav
    Middle name: Olsen
    Last name: Heggelien
    
    First name: Jan Olav
    Last name: Olsen Heggelien
    
    First name: Jan
    Middle name: Olav
    Last name: Olsen Heggelien
    
    SELECT 
      SUBSTRING(fullname, '(\\w+)') as firstname,
      SUBSTRING(fullname, '\\w+\\s(\\w+)\\s\\w+') as middle,
      COALESCE(SUBSTRING(fullname, '\\w+\\s\\w+\\s(\\w+)'), SUBSTRING(fullname, '\\w+\\s(\\w+)')) as lastname
    FROM 
    public.person
    
    正则表达式可能更简洁一些;但你明白了。顺便说一句,这对有两个双重名字的人不起作用(在荷兰,我们有很多“Jan van der Ploeg”),所以我会非常小心结果。

    就像#1所说的,这不是小事。连字符的姓氏、首字母、双名、逆名称序列和各种其他异常情况都会破坏精心设计的函数

    您可以使用第三方库(插件/免责声明-我曾使用过此产品):


    我会将此作为一个迭代过程

    1) 将表格转储到平面文件以进行处理

    2) 编写一个简单的程序,使用空格作为分隔符,其中firsts token是名字,如果有3个token,那么token2是中间名,token3是姓氏。如果有2个令牌,则第二个令牌是姓氏。(Perl、Java或C/C++,语言无关紧要)

    3) 仔细观察结果。查找不符合此规则的名称

    4) 使用该示例,创建一个新规则来处理该异常

    5) 冲洗并重复


    最终,您将得到一个修复所有数据的程序。

    这里是一个自包含的示例,具有易于操作的测试数据

    在本例中,如果您的姓名包含三个以上的部分,那么所有“额外”内容都将放在LAST_name字段中。对于标识为“标题”的特定字符串,例如“DR”、“MRS”和“MR”,则有例外

    如果缺少中间名,那么您只需要获得FIRST_name和LAST_name(中间名将为NULL)

    您可以将它粉碎成一个巨大的嵌套子字符串块,但可读性很难,就像在SQL中这样做一样

    编辑--处理以下特殊情况:

    1-名称字段为空

    2-名称字段包含前导/尾随空格

    3-名称字段在名称中有>1个连续空格

    4-名称字段仅包含名字

    5-为便于阅读,将原始全名作为单独一列包含在最终输出中

    6-将特定前缀列表作为单独的“标题”列处理

    SELECT
      FIRST_NAME.ORIGINAL_INPUT_DATA
     ,FIRST_NAME.TITLE
     ,FIRST_NAME.FIRST_NAME
     ,CASE WHEN 0 = CHARINDEX(' ',FIRST_NAME.REST_OF_NAME)
           THEN NULL  --no more spaces?  assume rest is the last name
           ELSE SUBSTRING(
                           FIRST_NAME.REST_OF_NAME
                          ,1
                          ,CHARINDEX(' ',FIRST_NAME.REST_OF_NAME)-1
                         )
           END AS MIDDLE_NAME
     ,SUBSTRING(
                 FIRST_NAME.REST_OF_NAME
                ,1 + CHARINDEX(' ',FIRST_NAME.REST_OF_NAME)
                ,LEN(FIRST_NAME.REST_OF_NAME)
               ) AS LAST_NAME
    FROM
      (  
      SELECT
        TITLE.TITLE
       ,CASE WHEN 0 = CHARINDEX(' ',TITLE.REST_OF_NAME)
             THEN TITLE.REST_OF_NAME --No space? return the whole thing
             ELSE SUBSTRING(
                             TITLE.REST_OF_NAME
                            ,1
                            ,CHARINDEX(' ',TITLE.REST_OF_NAME)-1
                           )
        END AS FIRST_NAME
       ,CASE WHEN 0 = CHARINDEX(' ',TITLE.REST_OF_NAME)  
             THEN NULL  --no spaces @ all?  then 1st name is all we have
             ELSE SUBSTRING(
                             TITLE.REST_OF_NAME
                            ,CHARINDEX(' ',TITLE.REST_OF_NAME)+1
                            ,LEN(TITLE.REST_OF_NAME)
                           )
        END AS REST_OF_NAME
       ,TITLE.ORIGINAL_INPUT_DATA
      FROM
        (   
        SELECT
          --if the first three characters are in this list,
          --then pull it as a "title".  otherwise return NULL for title.
          CASE WHEN SUBSTRING(TEST_DATA.FULL_NAME,1,3) IN ('MR ','MS ','DR ','MRS')
               THEN LTRIM(RTRIM(SUBSTRING(TEST_DATA.FULL_NAME,1,3)))
               ELSE NULL
               END AS TITLE
          --if you change the list, don't forget to change it here, too.
          --so much for the DRY prinicple...
         ,CASE WHEN SUBSTRING(TEST_DATA.FULL_NAME,1,3) IN ('MR ','MS ','DR ','MRS')
               THEN LTRIM(RTRIM(SUBSTRING(TEST_DATA.FULL_NAME,4,LEN(TEST_DATA.FULL_NAME))))
               ELSE LTRIM(RTRIM(TEST_DATA.FULL_NAME))
               END AS REST_OF_NAME
         ,TEST_DATA.ORIGINAL_INPUT_DATA
        FROM
          (
          SELECT
            --trim leading & trailing spaces before trying to process
            --disallow extra spaces *within* the name
            REPLACE(REPLACE(LTRIM(RTRIM(FULL_NAME)),'  ',' '),'  ',' ') AS FULL_NAME
           ,FULL_NAME AS ORIGINAL_INPUT_DATA
          FROM
            (
            --if you use this, then replace the following
            --block with your actual table
                  SELECT 'GEORGE W BUSH' AS FULL_NAME
            UNION SELECT 'SUSAN B ANTHONY' AS FULL_NAME
            UNION SELECT 'ALEXANDER HAMILTON' AS FULL_NAME
            UNION SELECT 'OSAMA BIN LADEN JR' AS FULL_NAME
            UNION SELECT 'MARTIN J VAN BUREN SENIOR III' AS FULL_NAME
            UNION SELECT 'TOMMY' AS FULL_NAME
            UNION SELECT 'BILLY' AS FULL_NAME
            UNION SELECT NULL AS FULL_NAME
            UNION SELECT ' ' AS FULL_NAME
            UNION SELECT '    JOHN  JACOB     SMITH' AS FULL_NAME
            UNION SELECT ' DR  SANJAY       GUPTA' AS FULL_NAME
            UNION SELECT 'DR JOHN S HOPKINS' AS FULL_NAME
            UNION SELECT ' MRS  SUSAN ADAMS' AS FULL_NAME
            UNION SELECT ' MS AUGUSTA  ADA   KING ' AS FULL_NAME      
            ) RAW_DATA
          ) TEST_DATA
        ) TITLE
      ) FIRST_NAME
    

    我曾经制作了一个500个字符的正则表达式来解析任意字符串中的名字、姓氏和中间名。即使使用了那个响亮的正则表达式,由于输入完全不一致,它的准确率也只有97%左右。尽管如此,总比什么都没有好。

    根据已经提出的关于名称中空格和其他异常的警告,下面的代码将至少处理98%的错误