web-dev-qa-db-fra.com

SQL: analyser le prénom, le prénom et le nom d'un champ de nom complet

Comment analyser le premier, le deuxième prénom et le nom de famille d'un champ de nom complet avec SQL? 

Je dois essayer de faire correspondre des noms qui ne correspondent pas directement au nom complet. J'aimerais pouvoir prendre le champ de nom complet et le diviser en prénom, deuxième prénom et nom de famille. 

Les données n'incluent aucun préfixe ou suffixe. Le deuxième prénom est facultatif. Les données sont formatées 'Premier milieu dernier'.

Je suis intéressé par des solutions pratiques pour me rendre à 90% du chemin. Comme il a été dit, il s’agit d’un problème complexe. Je traiterai donc les cas particuliers individuellement.

40
Even Mien

Voici un exemple autonome, avec des données de test faciles à manipuler. 

Avec cet exemple, si vous avez un nom avec plus de trois parties, tous les éléments "supplémentaires" seront placés dans le champ LAST_NAME. Une exception est faite pour des chaînes spécifiques identifiées comme "titres", telles que "DR", "MRS" et "MR".

Si le deuxième prénom est manquant, il suffit alors d'obtenir FIRST_NAME et LAST_NAME (MIDDLE_NAME sera NULL).

Vous pouvez le casser dans un blob imbriqué géant de SUBSTRING, mais la lisibilité est assez difficile à obtenir, comme c'est le cas lorsque vous effectuez cette opération dans SQL.

Edit-- Traitez les cas spéciaux suivants:

1 - Le champ NAME est NULL

2 - Le champ NAME contient les espaces de début/fin

3 - Le champ NAME contient> 1 espace consécutif dans le nom

4 - Le champ NAME contient SEULEMENT le prénom

5 - Inclure le nom complet original dans la sortie finale sous forme de colonne distincte, pour la lisibilité

6 - Gère une liste spécifique de préfixes dans une colonne "titre" séparée

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

Il est difficile de répondre sans savoir comment le "nom complet" est formaté.

Cela peut être "Nom de famille, prénom deuxième prénom" ou "Prénom deuxième prénom Nom de famille", etc.

Fondamentalement, vous devrez utiliser la fonctionSOUS-CHAÎNE

SUBSTRING ( expression , start , length )

Et probablement leCHARINDEXfonction 

CHARINDEX (substr, expression)

Déterminer le début et la longueur de chaque partie à extraire.

Alors disons que le format est "Prénom Nom", vous pouvez (non testé .. mais devrait être proche): 

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

Inversez le problème, ajoutez des colonnes pour contenir les pièces individuelles et combinez-les pour obtenir le nom complet.

La raison pour laquelle ce sera le meilleur réponse est qu’il n’existe aucun moyen de déterminer si une personne s’est enregistrée en tant que prénom et quel est son deuxième prénom.

Par exemple, comment diviseriez-vous cela?

Jan Olav Olsen Heggelien

Ceci, bien que fictif, est un nom légal en Norvège et pourrait, mais ne serait pas obligé, être scindé comme suit:

First name: Jan Olav
Middle name: Olsen
Last name: Heggelien

ou comme ceci:

First name: Jan Olav
Last name: Olsen Heggelien

ou comme ceci:

First name: Jan
Middle name: Olav
Last name: Olsen Heggelien

J'imagine que des événements similaires peuvent être trouvés dans la plupart des langues.

Ainsi, au lieu d'essayer d'interpréter des données qui ne contiennent pas assez d'informations pour bien les interpréter, stockez l'interprétation correcte et combinez-les pour obtenir le nom complet.

À moins que vous ne possédiez des données très, très sage, il s'agit d'un défi non trivial. Une approche naïve consisterait à définir des espaces et à supposer qu'un résultat à trois jetons est [premier, deuxième, dernier] et un résultat à deux jetons est [premier, dernier] Nom de famille Word (par exemple "Van Buren") et plusieurs prénoms.

6
Josh Millard

Une autre méthode simple consiste à utiliser 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

la source

4
hajili

Pour une solution libre basée sur le CLR SQL, assurez-vous de consulter SqlName de Ambient Concepts, qui peut s'avérer très utile pour l'analyse des noms au niveau de la base de données.

http://ambientconcepts.com/sqlname

3
Brice Williams

Etes-vous sûr que le nom légal complet inclura toujours les prénoms, les prénoms et les noms de famille? Je connais des personnes qui ont un seul nom comme Nom légal complet et, honnêtement, je ne suis pas sûr qu'il s'agisse de leur prénom ou de leur nom de famille. :-) Je connais aussi des personnes qui ont plus d'un prénom dans leur nom légal, mais qui n'ont pas de prénom. Et il y a des gens qui ont plusieurs prénoms.

Ensuite, il y a aussi l'ordre des noms dans le nom légal complet. Autant que je sache, dans certaines cultures asiatiques, le nom de famille vient en premier dans le nom légal complet.

Sur une note plus pratique, vous pouvez diviser le nom complet en espaces et menacer le premier jeton en tant que Prénom et le dernier jeton (ou le seul en cas de nom unique) en nom de famille. Bien que cela suppose que l'ordre sera toujours le même.

2
Franci Penov

Si vous essayez d'analyser un nom humain en PHP, je vous recommande script nameparse.php de Keith Beckman .

Copier en cas de panne du site: 

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

Comme n ° 1 dit, ce n'est pas trivial. Les noms de famille avec trait d'union, les initiales, les doubles noms, la séquence de nom inverse et diverses autres anomalies peuvent ruiner votre fonction soigneusement conçue.

Vous pouvez utiliser une bibliothèque tierce partie (plug/disclaimer - j'ai travaillé sur ce produit):

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

1
Marc Bernier

Cela fonctionnera dans le cas où la chaîne est Prénom/Prénom/Nom

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
1
Nikul

Je ferais cela comme un processus itératif. 

1) Videz la table dans un fichier plat avec lequel travailler.

2) Écrivez un programme simple pour séparer vos noms en utilisant un espace comme séparateur, le premier jeton étant le prénom, s'il y a 3 jetons, le jeton 2 étant le deuxième prénom et le jeton 3 le nom de famille. S'il y a 2 jetons, le deuxième jeton est le nom de famille. (Perl, Java ou C/C++, le langage n'a pas d'importance)

3) Eyeball les résultats. Recherchez les noms qui ne correspondent pas à cette règle. 

4) En utilisant cet exemple, créez une nouvelle règle pour gérer cette exception ...

5) rincer et répéter

Finalement, vous obtiendrez un programme qui corrigera toutes vos données.

1
Ben

Une fois, j'ai créé une expression régulière de 500 caractères pour analyser les prénoms, les prénoms et les prénoms d'une chaîne arbitraire. Même avec cette regex qui klaxonne, il n’atteint que 97% de précision en raison de l’incohérence totale des entrées. Encore mieux que rien.

0
boris

Sous réserve des mises en garde déjà exprimées concernant les espaces dans les noms et autres anomalies, le code suivant gérera au moins 98% des noms. (Remarque: SQL désordonné car je n'ai pas d'option regex dans la base de données que j'utilise.)

** Attention: SQL désordonné suit:

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

Le code fonctionne en créant une table temporaire (parsname) et en symbolisant le nom complet par des espaces. Tous les noms dont les valeurs sont nommées name3 ou name4 ne sont pas conformes et devront être traités différemment.

0
Kluge

Comme tout le monde le dit, vous ne pouvez pas le faire par programmation.

Considérez ces exemples:

  • Président "George Herbert Walker Bush" (Premier Moyen Dernier Dernier)

  • Assassin présidentiel "John Wilkes Booth" (Premier Milieu Dernier)

  • Le guitariste "Eddie Van Halen" (Premier Dernier Dernier)

  • Et sa mère l'appelle probablement Edward Lodewijk Van Halen (Premier Milieu Dernier Dernier)

  • "Mary Ann Summers" (Premier Premier Dernier)

  • Président du GOP du Nouveau Mexique "Fernando C de Baca" (Premier Dernier Dernier Dernier)

0
Andy Lester

Le plus gros problème que j'ai rencontré en faisant cela était des cas comme "Bob R. Smith, Jr.". L'algorithme que j'ai utilisé est publié à l'adresse http://www.blackbeltcoder.com/Articles/strings/splitting-a-name-into-first-and-last-names . Mon code est en C # mais vous pouvez le porter si vous devez avoir en SQL.

0
Jonathan Wood

Vérifiez cette requête dans Athena uniquement pour la chaîne séparée par un espace (par exemple, combinaison du prénom et du deuxième prénom):

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

Si vous prévoyez d’avoir deux espaces ou plus, vous pouvez facilement étendre la requête ci-dessus. 

0
James A.

Le travail de @JosephStyons et @Digs est excellent! J'ai utilisé certaines parties de leur travail pour créer une nouvelle fonction pour SQL Server 2016 et versions ultérieures. Celui-ci gère également les suffixes, ainsi que les préfixes.

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. Obtenir une fonction regex SQL. Exemple: http://msdn.Microsoft.com/en-us/magazine/cc163473.aspx
  2. Extraire les noms en utilisant des expressions régulières. 

Je recommande Expresso pour apprendre/construire/tester des expressions régulières. Ancienne version gratuite , nouvelle version commerciale

0
Bartek Szabat

Je ne suis pas sûr du serveur SQL, mais dans postgres, vous pourriez faire quelque chose comme ça:

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

Les expressions de regex pourraient probablement être un peu plus concises; mais vous obtenez le point. Soit dit en passant, cela ne fonctionne pas pour les personnes ayant deux prénoms doubles (aux Pays-Bas, nous avons souvent Jan Jan der der Ploeg);.

0
p3t0r

Bien sûr, nous comprenons tous qu’il n’existe pas de solution parfaite pour résoudre ce problème, mais certaines solutions peuvent vous amener plus loin que d’autres. 

En particulier, il est assez facile d'aller au-delà des simples séparateurs d'espaces, si vous avez juste quelques listes de préfixes communs (Mr, Dr, Mme, etc.), d'infixes (von, de, del, etc.), de suffixes (Jr, III , Sr, etc.) et ainsi de suite. Il est également utile si vous avez des listes de prénoms communs (dans différentes langues/cultures, si vos noms sont divers), afin de pouvoir deviner si un mot au milieu fera probablement partie du nom de famille ou non.

BibTeX implémente également des heuristiques qui vous permettent d’atteindre une partie du chemin; ils sont encapsulés dans le module Perl Text::BibTeX::Name. Voici un exemple de code rapide qui fait un travail raisonnable.

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

Voici une procédure stockée qui mettra le premier mot trouvé dans le nom, le dernier mot dans le nom et tout le reste dans le deuxième prénom.

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 

Et voici moi l'appelant.

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