SQL:从fullname字段解析first,middle和last名称

时间:2008-10-01 20:32:31

标签: sql sql-server tsql parsing

如何使用SQL解析fullname字段中的第一个,中间名和姓氏?

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

数据不包含任何前缀或后缀。中间名是可选的。数据格式为“First Middle Last”。

我对一些实用的解决方案感兴趣,让我90%的方式。如上所述,这是一个复杂的问题,因此我将单独处理特殊情况。

25 个答案:

答案 0 :(得分:130)

这是一个独立的示例,具有易于操作的测试数据。

使用此示例,如果您的名称包含三个以上的部分,则所有“额外”内容都将放入LAST_NAME字段中。标识为“标题”的特定字符串例外,例如“DR”,“MRS”和“MR”。

如果缺少中间名,那么您只能获得FIRST_NAME和LAST_NAME(MIDDLE_NAME将为NULL)。

你可以把它粉碎成一个巨大的嵌套的SUBSTRING blob,但可读性很难,就像你在SQL中这样做一样。

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

1 - NAME字段为NULL

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

3 - NAME字段包含>名称中的1个连续空格

4 - NAME字段仅包含名字

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

答案 1 :(得分:8)

如果不知道如何格式化“全名”,很难回答。

可能是“姓氏,名字中间名”或“名字中间名”等。

基本上你必须使用 SUBSTRING 功能

SUBSTRING ( expression , start , length )

可能是 CHARINDEX 功能

CHARINDEX (substr, expression)

确定要提取的每个部分的起点和长度。

所以让我们说格式是“名字姓”你可以(未经测试......但应该关闭):

SELECT 
SUBSTRING(fullname, 1, CHARINDEX(' ', fullname) - 1) AS FirstName, 
SUBSTRING(fullname, CHARINDEX(' ', fullname) + 1, len(fullname)) AS LastName
FROM YourTable

答案 2 :(得分:7)

撤消问题,添加列以保存各个部分并将它们组合起来以获取全名。

这将是最佳答案的原因是,没有保证可以找出一个人已经注册为他们的名字,以及他们的中间名是什么。

例如,你会如何拆分?

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

我认为在大多数语言中都可以找到类似的出现。

因此,不要试图解释没有足够信息来正确处理的数据,而是存储正确的解释,然后结合起来获取全名。

答案 3 :(得分:6)

除非你有非常非常好的数据,否则这是一个非常重要的挑战。一种天真的方法是在空格上进行标记化,并假设三个标记结果是[第一个,中间的,最后一个],并且双标记结果是[第一个,最后一个],但是你将不得不处理多个单词姓氏(例如“Van Buren”)和多个中间名。

答案 4 :(得分:4)

另一种简单的方法是使用parsename

select full_name,
   parsename(replace(full_name, ' ', '.'), 3) as FirstName,
   parsename(replace(full_name, ' ', '.'), 2) as MiddleName,
   parsename(replace(full_name, ' ', '.'), 1) as LastName 
from YourTableName

source

答案 5 :(得分:3)

对于基于SQL CLR的免费解决方案,请务必查看Ambient Concepts中的SqlName,这对于在数据库级别解析名称可能有很大帮助。

http://ambientconcepts.com/sqlname

答案 6 :(得分:2)

您确定完整法定名称将始终包括First,Middle和Last吗?我认识那些只有一个名字作为完整法定名称的人,老实说,我不确定这是他们的名字还是姓氏。 :-)我也知道在法定名称中有多个Fisrt名字的人,但没有中间名。还有一些人有多个中间名。

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

在更实际的说明中,您可以在空格上拆分全名,并将第一个标记作为名字和最后一个标记(或只有一个名称的唯一标记)作为姓氏。虽然这假设订单总是一样的。

答案 7 :(得分:1)

就像#1说的那样,这不是微不足道的。连字符,缩写,双重名称,反名称序列和各种其他异常可能会破坏您精心设计的功能。

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

http://www.melissadata.com/nameobject/nameobject.htm

答案 8 :(得分:1)

我会这样做是一个迭代过程。

1)将表转储到平面文件中以便使用。

2)编写一个简单的程序,使用空格作为分隔符来分解你的名字,其中第一个是第一个名字,如果有3个令牌,则令牌2是中间名,令牌3是姓。如果有2个令牌,则第二个令牌是姓氏。 (Perl,Java或C / C ++,语言并不重要)

3)眼球结果。查找不符合此规则的名称。

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

5)冲洗并重复

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

答案 9 :(得分:1)

这将在Case String中工作是FirstName / MiddleName / LastName

Select 

DISTINCT NAMES ,

   SUBSTRING(NAMES , 1, CHARINDEX(' ', NAMES) - 1) as FirstName,

   RTRIM(LTRIM(REPLACE(REPLACE(NAMES,SUBSTRING(NAMES , 1, CHARINDEX(' ', NAMES) - 1),''),REVERSE( LEFT( REVERSE(NAMES), CHARINDEX(' ', REVERSE(NAMES))-1 ) ),'')))as MiddleName,

   REVERSE( LEFT( REVERSE(NAMES), CHARINDEX(' ', REVERSE(NAMES))-1 ) ) as LastName

From TABLENAME

答案 10 :(得分:1)

如果您尝试在PHP中解析人名,我建议Keith Beckman's nameparse.php script

如果网站出现故障,请复制:

<?
/*
Name:   nameparse.php
Version: 0.2a
Date:   030507
First:  030407
License:    GNU General Public License v2
Bugs:   If one of the words in the middle name is Ben (or St., for that matter),
        or any other possible last-name prefix, the name MUST be entered in
        last-name-first format. If the last-name parsing routines get ahold
        of any prefix, they tie up the rest of the name up to the suffix. i.e.:

        William Ben Carey   would yield 'Ben Carey' as the last name, while,
        Carey, William Ben  would yield 'Carey' as last and 'Ben' as middle.

        This is a problem inherent in the prefix-parsing routines algorithm,
        and probably will not be fixed. It's not my fault that there's some
        odd overlap between various languages. Just don't name your kids
        'Something Ben Something', and you should be alright.

*/

function    norm_str($string) {
    return  trim(strtolower(
        str_replace('.','',$string)));
    }

function    in_array_norm($needle,$haystack) {
    return  in_array(norm_str($needle),$haystack);
    }

function    parse_name($fullname) {
    $titles         =   array('dr','miss','mr','mrs','ms','judge');
    $prefices       =   array('ben','bin','da','dal','de','del','der','de','e',
                            'la','le','san','st','ste','van','vel','von');
    $suffices       =   array('esq','esquire','jr','sr','2','ii','iii','iv');

    $pieces         =   explode(',',preg_replace('/\s+/',' ',trim($fullname)));
    $n_pieces       =   count($pieces);

    switch($n_pieces) {
        case    1:  // array(title first middles last suffix)
            $subp   =   explode(' ',trim($pieces[0]));
            $n_subp =   count($subp);
            for($i = 0; $i < $n_subp; $i++) {
                $curr               =   trim($subp[$i]);
                $next               =   trim($subp[$i+1]);

                if($i == 0 && in_array_norm($curr,$titles)) {
                    $out['title']   =   $curr;
                    continue;
                    }

                if(!$out['first']) {
                    $out['first']   =   $curr;
                    continue;
                    }

                if($i == $n_subp-2 && $next && in_array_norm($next,$suffices)) {
                    if($out['last']) {
                        $out['last']    .=  " $curr";
                        }
                    else {
                        $out['last']    =   $curr;
                        }
                    $out['suffix']      =   $next;
                    break;
                    }

                if($i == $n_subp-1) {
                    if($out['last']) {
                        $out['last']    .=  " $curr";
                        }
                    else {
                        $out['last']    =   $curr;
                        }
                    continue;
                    }

                if(in_array_norm($curr,$prefices)) {
                    if($out['last']) {
                        $out['last']    .=  " $curr";
                        }
                    else {
                        $out['last']    =   $curr;
                        }
                    continue;
                    }

                if($next == 'y' || $next == 'Y') {
                    if($out['last']) {
                        $out['last']    .=  " $curr";
                        }
                    else {
                        $out['last']    =   $curr;
                        }
                    continue;
                    }

                if($out['last']) {
                    $out['last']    .=  " $curr";
                    continue;
                    }

                if($out['middle']) {
                    $out['middle']      .=  " $curr";
                    }
                else {
                    $out['middle']      =   $curr;
                    }
                }
            break;
        case    2:
                switch(in_array_norm($pieces[1],$suffices)) {
                    case    TRUE: // array(title first middles last,suffix)
                        $subp   =   explode(' ',trim($pieces[0]));
                        $n_subp =   count($subp);
                        for($i = 0; $i < $n_subp; $i++) {
                            $curr               =   trim($subp[$i]);
                            $next               =   trim($subp[$i+1]);

                            if($i == 0 && in_array_norm($curr,$titles)) {
                                $out['title']   =   $curr;
                                continue;
                                }

                            if(!$out['first']) {
                                $out['first']   =   $curr;
                                continue;
                                }

                            if($i == $n_subp-1) {
                                if($out['last']) {
                                    $out['last']    .=  " $curr";
                                    }
                                else {
                                    $out['last']    =   $curr;
                                    }
                                continue;
                                }

                            if(in_array_norm($curr,$prefices)) {
                                if($out['last']) {
                                    $out['last']    .=  " $curr";
                                    }
                                else {
                                    $out['last']    =   $curr;
                                    }
                                continue;
                                }

                            if($next == 'y' || $next == 'Y') {
                                if($out['last']) {
                                    $out['last']    .=  " $curr";
                                    }
                                else {
                                    $out['last']    =   $curr;
                                    }
                                continue;
                                }

                            if($out['last']) {
                                $out['last']    .=  " $curr";
                                continue;
                                }

                            if($out['middle']) {
                                $out['middle']      .=  " $curr";
                                }
                            else {
                                $out['middle']      =   $curr;
                                }
                            }                       
                        $out['suffix']  =   trim($pieces[1]);
                        break;
                    case    FALSE: // array(last,title first middles suffix)
                        $subp   =   explode(' ',trim($pieces[1]));
                        $n_subp =   count($subp);
                        for($i = 0; $i < $n_subp; $i++) {
                            $curr               =   trim($subp[$i]);
                            $next               =   trim($subp[$i+1]);

                            if($i == 0 && in_array_norm($curr,$titles)) {
                                $out['title']   =   $curr;
                                continue;
                                }

                            if(!$out['first']) {
                                $out['first']   =   $curr;
                                continue;
                                }

                        if($i == $n_subp-2 && $next &&
                            in_array_norm($next,$suffices)) {
                            if($out['middle']) {
                                $out['middle']  .=  " $curr";
                                }
                            else {
                                $out['middle']  =   $curr;
                                }
                            $out['suffix']      =   $next;
                            break;
                            }

                        if($i == $n_subp-1 && in_array_norm($curr,$suffices)) {
                            $out['suffix']      =   $curr;
                            continue;
                            }

                        if($out['middle']) {
                            $out['middle']      .=  " $curr";
                            }
                        else {
                            $out['middle']      =   $curr;
                            }
                        }
                        $out['last']    =   $pieces[0];
                        break;
                    }
            unset($pieces);
            break;
        case    3:  // array(last,title first middles,suffix)
            $subp   =   explode(' ',trim($pieces[1]));
            $n_subp =   count($subp);
            for($i = 0; $i < $n_subp; $i++) {
                $curr               =   trim($subp[$i]);
                $next               =   trim($subp[$i+1]);
                if($i == 0 && in_array_norm($curr,$titles)) {
                    $out['title']   =   $curr;
                    continue;
                    }

                if(!$out['first']) {
                    $out['first']   =   $curr;
                    continue;
                    }

                if($out['middle']) {
                    $out['middle']      .=  " $curr";
                    }
                else {
                    $out['middle']      =   $curr;
                    }
                }

            $out['last']                =   trim($pieces[0]);
            $out['suffix']              =   trim($pieces[2]);
            break;
        default:    // unparseable
            unset($pieces);
            break;
        }

    return $out;
    }


?>

答案 11 :(得分:0)

请尝试以下查询,它为我工作:雇员表具有“名称”列,我们不得不将其拆分为名字,中间名和姓氏。如果名称列的值是两个单词(例如“詹姆斯·托马斯”),则该查询还将处理将中间名保持为空。

更新员工设置[名字] =当(len(name)-len(Replace(name,'。','')))= 2时的大小写THEN PARSENAME(Name,3)当(len(name)- len(Replace(name,'。','')))= 1 THEN PARSENAME(Name,2)ELSE PARSENAME(Name,1)END,[Middle Name] = Case when(len(name)-len(Replace( name,'。',''))))= 2 THEN PARSENAME(Name,2)否则为空END,[姓氏] =当(len(name)-len(Replace(name,'。','')) ))= 2 THEN PARSENAME(Name,1)当(len(name)-len(Replace(name,'。',''))))= 1 THEN PARSENAME(Name,1)其他空END

GO 更新员工集[名称] =替换([名称],'。','') 开始

答案 12 :(得分:0)

答案

此查询工作正常。

选择名称, Ltrim(SubString(name,1,Isnull(Nullif(CHARINDEX('',name),0),1000)))作为名字,

Ltrim(SUBSTRING(name,CharIndex('',name),CAse when(CHARINDEX('',name,CHARINDEX('',name)+1)-CHARINDEX('',name))<= 0然后0 else CHARINDEX('',name,CHARINDEX('',name)+1)-CHARINDEX('',name)end))作为MiddleName,

Ltrim(SUBSTRING(name,Isnull(Nullif(CHARINDEX('',name,Charindex('',name)+1),0),CHARINDEX('',name)),当Charindex('', name)= 0,然后返回0,否则LEN(name)end))作为姓氏

来自yourtableName

谢谢 穆克什

答案 13 :(得分:0)

基于@hajili的贡献(这是对parsename函数的创造性使用,旨在解析句点分隔的对象的名称),我对其进行了修改,以便可以处理数据不包含以下内容的情况:中间名或当名称是“ John and Jane Doe”时。它不是100%完美,但结构紧凑,可以根据业务案例来解决问题。

$datatable.find("tbody").on("dblclick", "tr", function() {
  window.location = '{{route("Documento.download")}}';
});

答案 14 :(得分:0)

在Athena中检查此查询仅查找一个空格分隔的字符串(例如,名字和中间名称组合):

SELECT name, REVERSE( SUBSTR( REVERSE(name), 1, STRPOS(REVERSE(name), ' ') ) ) AS middle_name FROM name_table

如果您希望有两个或更多空格,则可以轻松扩展上述查询。

答案 15 :(得分:0)

@JosephStyons和@Digs的作品很棒!我使用他们的部分工作为SQL Server 2016及更新版本创建了一个新功能。这个也处理后缀和前缀。

CREATE FUNCTION [dbo].[NameParser]
(
    @name nvarchar(100)
)
RETURNS TABLE
AS
RETURN (

WITH prep AS (
    SELECT 
        original = @name,
        cleanName = REPLACE(REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(@name)),'  ',' '),'  ',' '), '.', ''), ',', '')
)
SELECT
    prep.original,
    aux.prefix,
    firstName.firstName,
    middleName.middleName,
    lastName.lastName,
    aux.suffix
FROM
    prep
    CROSS APPLY (
        SELECT 
            prefix =
                CASE 
                    WHEN LEFT(prep.cleanName, 3) IN ('MR ', 'MS ', 'DR ', 'FR ')
                        THEN LEFT(prep.cleanName, 2)
                    WHEN LEFT(prep.cleanName, 4) IN ('MRS ', 'LRD ', 'SIR ')
                        THEN LEFT(prep.cleanName, 3)
                    WHEN LEFT(prep.cleanName, 5) IN ('LORD ', 'LADY ', 'MISS ', 'PROF ')
                        THEN LEFT(prep.cleanName, 4)
                    ELSE ''
                END,
            suffix =
                CASE 
                    WHEN RIGHT(prep.cleanName, 3) IN (' JR', ' SR', ' II', ' IV')
                        THEN RIGHT(prep.cleanName, 2)
                    WHEN RIGHT(prep.cleanName, 4) IN (' III', ' ESQ')
                        THEN RIGHT(prep.cleanName, 3)
                    ELSE ''
                END
    ) aux
    CROSS APPLY (
        SELECT
            baseName = LTRIM(RTRIM(SUBSTRING(prep.cleanName, LEN(aux.prefix) + 1, LEN(prep.cleanName) - LEN(aux.prefix) - LEN(aux.suffix)))),
            numParts = (SELECT COUNT(1) FROM STRING_SPLIT(LTRIM(RTRIM(SUBSTRING(prep.cleanName, LEN(aux.prefix) + 1, LEN(prep.cleanName) - LEN(aux.prefix) - LEN(aux.suffix)))), ' '))
    ) core
    CROSS APPLY (
        SELECT
            firstName = 
                CASE
                    WHEN core.numParts <= 1 THEN core.baseName
                    ELSE LEFT(core.baseName, CHARINDEX(' ', core.baseName, 1) - 1) 
                END

    ) firstName
    CROSS APPLY (
        SELECT
            remainder = 
                CASE
                    WHEN core.numParts <= 1 THEN ''
                    ELSE LTRIM(SUBSTRING(core.baseName, LEN(firstName.firstName) + 1, 999999))
                END
    ) work1
    CROSS APPLY (
        SELECT
            middleName = 
                CASE
                    WHEN core.numParts <= 2 THEN ''
                    ELSE LEFT(work1.remainder, CHARINDEX(' ', work1.remainder, 1) - 1) 
                END
    ) middleName
    CROSS APPLY (
        SELECT
            lastName = 
                CASE
                    WHEN core.numParts <= 1 THEN ''
                    ELSE LTRIM(SUBSTRING(work1.remainder, LEN(middleName.middleName) + 1, 999999))
                END
    ) lastName
)

GO

SELECT * FROM dbo.NameParser('Madonna')
SELECT * FROM dbo.NameParser('Will Smith')
SELECT * FROM dbo.NameParser('Neil Degrasse Tyson')
SELECT * FROM dbo.NameParser('Dr. Neil Degrasse Tyson')
SELECT * FROM dbo.NameParser('Mr. Hyde')
SELECT * FROM dbo.NameParser('Mrs. Thurston Howell, III')

答案 16 :(得分:0)

我想发布对 hajili 建议的更新,但此回复太长,无法对该建议发表评论。

我们的问题是“姓氏,名字中间名”,其中一些姓氏中带有空格。

所以我们想出了:

,FullName           = CUST.FULLNAME
,LastName           = PARSENAME(REPLACE(CUST.FULLNAME, ',', '.'),2)
,FirstName          = (CASE WHEN PARSENAME(REPLACE(CUST.FULLNAME, ',', '.'),1) LIKE '% %' THEN PARSENAME(REPLACE(PARSENAME(REPLACE(CUST.FULLNAME, ',', '.'),1), ' ', '.'),2) ELSE PARSENAME(REPLACE(CUST.FULLNAME, ',', '.'),1) END)
,MiddleName         = (CASE WHEN PARSENAME(REPLACE(CUST.FULLNAME, ' ', '.'),1) LIKE '%,%' THEN NULL ELSE PARSENAME(REPLACE(CUST.FULLNAME, ' ', '.'),1) END)

答案 17 :(得分:0)

我遇到的最大问题是像“Bob R. Smith,Jr。”这样的案件。我使用的算法发布在http://www.blackbeltcoder.com/Articles/strings/splitting-a-name-into-first-and-last-names。我的代码在C#中,但如果你必须在SQL中,你可以移植它。

答案 18 :(得分:0)

我们当然都明白,没有完美的方法来解决这个问题,但有些解决方案可以让你比其他解决方案更远。

特别是,如果你只有一些共同的前缀(先生,博士,夫人等),中缀(von,de,del等),后缀(等等),那么超越简单的空白分割器就很容易了。 Jr,III,Sr等)等。如果您有一些常用名字列表(在各种语言/文化中,如果您的名字不同),这也很有用,这样您就可以猜测中间的单词是否可能是姓氏的一部分。

BibTeX还实现了一些启发式功能,可以帮助您实现目标;它们被封装在Text::BibTeX::Name perl模块中。这是一个合理的快速代码示例。

use Text::BibTeX;
use Text::BibTeX::Name;
$name = "Dr. Mario Luis de Luigi Jr.";
$name =~ s/^\s*([dm]rs?.?|miss)\s+//i;
$dr=$1;
$n=Text::BibTeX::Name->new($name);
print join("\t", $dr, map "@{[ $n->part($_) ]}", qw(first von last jr)), "\n";

答案 19 :(得分:0)

正如其他人所说,你不能以简单的程序化方式。

请考虑以下示例:

  • 总统“乔治赫伯特沃克布什”(中间最后一个中期)

  • 总统刺客“John Wilkes Booth”(First Middle 最后)

  • 吉他手“Eddie Van Halen”(前一个最后一个)

  • 他的妈妈可能称他为Edward Lodewijk Van Halen(第一名 最后一个中期)

  • 着名的被抛弃的“玛丽安萨默斯”(First First Last)

  • New Mexico GOP chairman“Fernando C de Baca”(前一个最后的最后一个)

答案 20 :(得分:0)

这是一个存储过程,它将第一个单词放入名字,最后一个单词放入姓氏,其间的所有内容放入中间名。

create procedure [dbo].[import_ParseName]
(            
    @FullName nvarchar(max),
    @FirstName nvarchar(255) output,
    @MiddleName nvarchar(255) output,
    @LastName nvarchar(255)  output
)
as
begin

set @FirstName = ''
set @MiddleName = ''
set @LastName = ''  
set @FullName = ltrim(rtrim(@FullName))

declare @ReverseFullName nvarchar(max)
set @ReverseFullName = reverse(@FullName)

declare @lengthOfFullName int
declare @endOfFirstName int
declare @beginningOfLastName int

set @lengthOfFullName = len(@FullName)
set @endOfFirstName = charindex(' ', @FullName)
set @beginningOfLastName = @lengthOfFullName - charindex(' ', @ReverseFullName) + 1

set @FirstName = case when @endOfFirstName <> 0 
                      then substring(@FullName, 1, @endOfFirstName - 1) 
                      else ''
                 end

set @MiddleName = case when (@endOfFirstName <> 0 and @beginningOfLastName <> 0 and @beginningOfLastName > @endOfFirstName)
                       then ltrim(rtrim(substring(@FullName, @endOfFirstName , @beginningOfLastName - @endOfFirstName))) 
                       else ''
                  end

set @LastName = case when @beginningOfLastName <> 0 
                     then substring(@FullName, @beginningOfLastName + 1 , @lengthOfFullName - @beginningOfLastName)
                     else ''
                end

return

end 

这就是我的称呼。

DECLARE @FirstName nvarchar(255),
        @MiddleName nvarchar(255),
        @LastName nvarchar(255)

EXEC    [dbo].[import_ParseName]
        @FullName = N'Scott The Other Scott Kowalczyk',
        @FirstName = @FirstName OUTPUT,
        @MiddleName = @MiddleName OUTPUT,
        @LastName = @LastName OUTPUT

print   @FirstName 
print   @MiddleName
print   @LastName 

output:

Scott
The Other Scott
Kowalczyk

答案 21 :(得分:0)

根据已经针对名称和其他异常中的空格提出的警告,以下代码将至少处理98%的名称。 (注意:凌乱的SQL,因为我在我使用的数据库中没有正则表达式选项。)

**警告:凌乱的SQL如下:

create table parsname (fullname char(50), name1 char(30), name2 char(30), name3 char(30), name4 char(40));
insert into parsname (fullname) select fullname from ImportTable;
update parsname set name1 = substring(fullname, 1, locate(' ', fullname)),
 fullname = ltrim(substring(fullname, locate(' ', fullname), length(fullname)))
 where locate(' ', rtrim(fullname)) > 0;
update parsname set name2 = substring(fullname, 1, locate(' ', fullname)),
 fullname = ltrim(substring(fullname, locate(' ', fullname), length(fullname)))
 where locate(' ', rtrim(fullname)) > 0;
update parsname set name3 = substring(fullname, 1, locate(' ', fullname)),
 fullname = ltrim(substring(fullname, locate(' ', fullname), length(fullname)))
 where locate(' ', rtrim(fullname)) > 0;
update parsname set name4 = substring(fullname, 1, locate(' ', fullname)),
 fullname = ltrim(substring(fullname, locate(' ', fullname), length(fullname)))
 where locate(' ', rtrim(fullname)) > 0;
// fullname now contains the last word in the string.
select fullname as FirstName, '' as MiddleName, '' as LastName from parsname where fullname is not null and name1 is null and name2 is null
union all
select name1 as FirstName, name2 as MiddleName, fullname as LastName from parsname where name1 is not null and name3 is null

代码的工作原理是创建一个临时表(parsname)并用空格标记全名。任何以name3或name4中的值结尾的名称都是不符合的,需要以不同方式处理。

答案 22 :(得分:0)

我曾经创建了一个500字符的正则表达式来解析来自任意字符串的第一个,最后一个和中间名。即使使用那种鸣笛正则表达式,由于输入完全不一致,它只能达到97%的准确度。仍然,总比没有好。

答案 23 :(得分:0)

我不确定SQL服务器,但是在postgres中你可以这样做:

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')所以我对结果非常小心。

答案 24 :(得分:0)

  1. 获取sql正则表达式函数。示例:http://msdn.microsoft.com/en-us/magazine/cc163473.aspx
  2. 使用正则表达式提取名称。
  3. 我推荐Expresso用于学习/构建/测试正则表达式。 Old free versionnew commercial version