web-dev-qa-db-ja.com

SQL:氏名フィールドから名、ミドルネーム、ラストネームを解析します

SQLでフルネームフィールドから名、ミドルネーム、ラストネームを解析するにはどうすればよいですか?

氏名と直接一致しない名前で照合する必要があります。氏名フィールドを取得して、姓、ミドルネーム、および姓に分割できるようにしたいと思います。

データには、プレフィックスまたはサフィックスは含まれません。ミドルネームはオプションです。データは「First Middle Last」の形式になっています。

90%の道を歩むための実用的なソリューションに興味があります。既に述べたように、これは複雑な問題なので、特別なケースを個別に処理します。

42
Even Mien

以下に、簡単に操作できるテストデータを使用した自己完結型の例を示します。

この例では、3つ以上の部分を持つ名前がある場合、すべての「余分な」ものがLAST_NAMEフィールドに入れられます。 「DR」、「MRS」、「MR」など、「タイトル」として識別される特定の文字列については例外が作成されます。

ミドルネームが欠落している場合、FIRST_NAMEとLAST_NAMEを取得するだけです(MIDDLE_NAMEはNULLになります)。

これをSUBSTRINGのネストされた巨大なBLOBに粉砕することもできますが、SQLでこれを行うときのように読みやすさは十分に困難です。

編集-以下の特別なケースを処理します:

1-NAMEフィールドはNULL

2-NAMEフィールドには先頭/末尾のスペースが含まれます

-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
132
JosephStyons

「フルネーム」がどのようにフォーマットされているかを知らずに答えることは困難です。

「姓、名、ミドルネーム」、「名、ミドルネーム、姓」などです。

基本的に、[〜#〜] 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
8
neonski

問題を逆転させ、列を追加して個々のピースを保持し、それらを結合して完全な名前を取得します。

これがbestの答えになる理由は、人が自分の名として登録したこと、およびミドルネームが何であるかを確認する方法が保証されていないためです。

たとえば、これをどのように分割しますか?

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トークンの結果が[first、middle、last]であり、2トークンの結果が[first、last]であると仮定することですが、複数の単語の姓(「Van Buren」など)および複数のミドルネーム。

7
Josh Millard

別の簡単な方法は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

ソース

6
hajili

フルリーガルネームには常にFirst、Middle、Lastが含まれますか?正式な名前として名前が1つしかない人は知っていますが、正直なところ、それが名か姓かはわかりません。 :-)また、正式な名前に複数のFisrt名があるが、ミドルネームを持っていない人も知っています。また、複数のミドルネームを持つ人もいます。

次に、正式名称の名前の順序もあります。私の知る限り、アジアの一部の文化では、姓が正式な正式名で最初にきます。

より実用的な注意として、フルネームを空白で分割し、最初のトークンを名として、最後のトークン(または名前が1つだけの場合は唯一のトークン)を姓として脅かすことができます。ただし、これは順序が常に同じであると想定しています。

2
Franci Penov

これは、文字列が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
2
user2877627

PHPで人間の名前を解析する場合は、 Keith Beckmanのnameparse.phpスクリプト をお勧めします。

サイトがダウンした場合のコピー:

<?
/*
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;
    }


?>
1
Jonathon Hill

#1が言ったように、それは簡単ではありません。ハイフンでつながれた姓、イニシャル、二重名、逆の名前シーケンス、およびその他のさまざまな異常により、慎重に作成された機能が台無しになる可能性があります。

サードパーティのライブラリを使用できます(プラグ/免責事項-私はこの製品で作業しました):

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

1
Marc Bernier

これは反復プロセスとして行います。

1)テーブルをフラットファイルにダンプして操作します。

2)区切り文字としてスペースを使用して名前を分割する簡単なプログラムを作成します。ここで、firstsトークンは名、3トークンの場合、トークン2はミドルネーム、トークン3は姓です。 2つのトークンがある場合、2番目のトークンは姓です。 (Perl、Java、またはC/C++、言語は関係ありません)

3)結果を確認します。このルールに合わない名前を探してください。

4)その例を使用して、その例外を処理する新しいルールを作成します...

5)すすぎと繰り返し

最終的には、すべてのデータを修正するプログラムを入手できます。

1
Ben

私はかつて、任意の文字列から名、姓、ミドルネームを解析するために500文字の正規表現を作成しました。その警鐘を鳴らす正規表現を使用しても、入力の完全な不整合により、約97%の精度しか得られませんでした。それでも、何もないよりはましです。

0
boris

名前のスペースやその他の異常に関してすでに提起されている警告を条件として、次のコードは少なくとも名前の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の値で終わる名前はすべて不適合であり、異なる方法で処理する必要があります。

0
Kluge

従業員表には列「Name」があり、それを名、ミドル、および姓に分割する必要がありました。このクエリは、名前列に「James Thomas」のような2つの単語の値がある場合、ミドルネームをnullとして保持します。

UPDATE Employees
SET [First Name] = CASE 
        WHEN (len(name) - len(Replace(name, '.', ''))) = 2
            THEN PARSENAME(Name, 3)
        WHEN (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)
        ELSE NULL
        END
    ,[Last Name] = CASE 
        WHEN (len(name) - len(Replace(name, '.', ''))) = 2
            THEN PARSENAME(Name, 1)
        WHEN (len(name) - len(Replace(name, '.', ''))) = 1
            THEN PARSENAME(Name, 1)
        ELSE NULL
        END GO

UPDATE Employee
SET [Name] = Replace([Name], '.', ' ') GO
0
Vinay Maurya

他の皆が言うように、あなたは簡単なプログラム的な方法からはできません。

これらの例を考慮してください:

  • 大統領「ジョージ・ハーバート・ウォーカー・ブッシュ」(最初のミドル・ミドル・ラスト)

  • 大統領暗殺者「ジョン・ウィルクス・ブース」(最初のミドル・ラスト)

  • ギタリスト「エディ・ヴァン・ヘイレン」(ファーストラストラスト)

  • そして、彼のお母さんはおそらく彼をエドワード・ローデワイク・ヴァン・ヘイレンと呼んでいます

  • 有名なキャストアウェイ「メアリー・アン・サマーズ」(ファースト・ファースト・ラスト)

  • ニューメキシコGOP議長 "フェルナンド・C・デ・バカ"(最初最後最後最後)

0
Andy Lester

このクエリは正常に機能しています。

SELECT name
    ,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 yourtableName
0
Mukesh Pandey

私がこれに遭遇した最大の問題は、「ボブ・R・スミス・ジュニア」のようなケースでした。使用したアルゴリズムは http://www.blackbeltcoder.com/Articles/strings/splitting-a-name-into-first-and-last-names に投稿されています。私のコードはC#ですが、SQLを使用する必要がある場合は移植できます。

0
Jonathan Wood

Athenaでこのクエリをチェックし、スペースで区切られた1つの文字列のみ(たとえば、名とミドルネームの組み合わせ)を確認します。

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

2つ以上のスペースが必要な場合は、上記のクエリを簡単に拡張できます。

0
James A.

@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')
0
  1. SQL正規表現関数を取得します。サンプル: http://msdn.Microsoft.com/en-us/magazine/cc163473.aspx
  2. 正規表現を使用して名前を抽出します。

正規表現の学習/構築/テストにはExpressoをお勧めします。 旧無料版新商用版

0
Bartek Szabat

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

正規表現はおそらくもう少し簡潔かもしれません。しかし、あなたはポイントを得る。ちなみに、これは2つの二重の名前を持つ人には機能しません(オランダではこれが多く「Jan van der Ploeg」です)、結果に非常に注意します。

0
p3t0r

もちろん、この問題を解決する完璧な方法はないということは誰もが理解していますが、いくつかの解決策は他の解決策よりもあなたを遠くまで導くことができます。

特に、共通の接頭辞(Mr、Dr、Mrsなど)、中置記号(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";
0
Ken Williams

見つかった最初のWordを名、最後のWordを姓、その間のすべてをミドルネームに格納するストアドプロシージャを次に示します。

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
0
Even Mien

@hajiliの貢献に基づいて(これは、期間で区切られたオブジェクトの名前を解析することを目的としたparsename関数の創造的な使用です)、データにミドルネームが含まれていない場合や名前が「John and Jane Doe」の場合。 100%完全ではありませんが、コンパクトであり、ビジネスケースによってはうまくいくかもしれません。

SELECT NAME,
CASE WHEN parsename(replace(NAME, ' ', '.'), 4) IS NOT NULL THEN 
   parsename(replace(NAME, ' ', '.'), 4) ELSE
    CASE WHEN parsename(replace(NAME, ' ', '.'), 3) IS NOT NULL THEN 
    parsename(replace(NAME, ' ', '.'), 3) ELSE
   parsename(replace(NAME, ' ', '.'), 2) end END as FirstName
   ,
CASE WHEN parsename(replace(NAME, ' ', '.'), 3) IS NOT NULL THEN 
   parsename(replace(NAME, ' ', '.'), 2) ELSE NULL END as MiddleName,
   parsename(replace(NAME, ' ', '.'), 1) as LastName
from  {@YourTableName}
0
Gustavo Lopez