try_parse - SQL: analiza el primer, segundo y segundo nombre de un campo de nombre completo
sql server try_parse (21)
- Obtenga una función de expresión regular sql. Muestra: http://msdn.microsoft.com/en-us/magazine/cc163473.aspx
- Extrae nombres usando expresiones regulares.
Recomiendo Expresso para aprender / construir / probar expresiones regulares. Antigua versión gratuita , nueva versión comercial
¿Cómo puedo analizar el primer, segundo y último nombre de un campo de nombre completo con SQL?
Necesito tratar de hacer coincidir los nombres que no coinciden directamente con el nombre completo. Me gustaría poder tomar el campo de nombre completo y dividirlo en primer, segundo y segundo nombre.
Los datos no incluyen ningún prefijo o sufijo. El segundo nombre es opcional. Los datos tienen el formato ''First Middle Last''.
Estoy interesado en algunas soluciones prácticas para obtener el 90% del camino hasta allí. Como se ha dicho, este es un problema complejo, por lo que manejaré casos especiales de forma individual.
¡El trabajo de @JosephStyons y @Digs es genial! Utilicé partes de su trabajo para crear una nueva función para SQL Server 2016 y posteriores. Este también maneja los sufijos, así como los prefijos.
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'')
¿Seguro que el Nombre legal completo siempre incluirá Primero, Medio y Último? Conozco personas que solo tienen un nombre como Full Legal Name, y sinceramente no estoy seguro de si ese es su nombre o apellido. :-) También conozco personas que tienen más de un nombre de Fisrt en su nombre legal, pero que no tienen un segundo nombre. Y hay algunas personas que tienen varios nombres medios.
Luego también está el orden de los nombres en el Nombre Legal Completo. Hasta donde yo sé, en algunas culturas asiáticas, el apellido viene primero en el nombre legal completo.
En una nota más práctica, podría dividir el Nombre completo en espacios en blanco y amenazar el primer token como Nombre y el último token (o el único token en el caso de un solo nombre) como Apellido. Aunque esto supone que el orden será siempre el mismo.
A menos que tenga datos muy, muy bien educados, este es un desafío no trivial. Un enfoque ingenuo sería tokenizar en espacios en blanco y asumir que un resultado de tres tokens es [primero, medio, último] y un resultado de dos tokens es [primero, el último], pero vas a tener que lidiar con múltiples apellidos (por ejemplo, "Van Buren") y varios apellidos intermedios.
Aquí hay un ejemplo autónomo, con datos de prueba fácilmente manipulados.
Con este ejemplo, si tiene un nombre con más de tres partes, todas las cosas "adicionales" se incluirán en el campo LAST_NAME. Se hace una excepción para cadenas específicas que se identifican como "títulos", como "DR", "MRS" y "MR".
Si falta el segundo nombre, acaba de obtener FIRST_NAME y LAST_NAME (MIDDLE_NAME será NULL).
Podrías romperlo en una masa gigante anidada de SUBSTRING, pero la legibilidad es lo suficientemente difícil como lo es cuando haces esto en SQL.
Editar-- Maneje los siguientes casos especiales:
1 - El campo NAME es NULL
2 - El campo NAME contiene espacios iniciales / finales
3 - El campo NOMBRE tiene> 1 espacio consecutivo dentro del nombre
4 - El campo NOMBRE contiene SOLAMENTE el primer nombre
5 - Incluye el nombre completo original en el resultado final como una columna separada, para la legibilidad
6 - Manejar una lista específica de prefijos como una columna separada de "título"
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
Aquí hay un procedimiento almacenado que colocará la primera palabra encontrada en Nombre, la última palabra en Apellido y todo lo intermedio en el Segundo nombre.
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
Y aquí estoy yo llamándolo.
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
Como dijo # 1, no es trivial. Los apellidos, iniciales, nombres dobles, secuencia inversa de nombres y una variedad de otras anomalías pueden arruinar su función cuidadosamente diseñada.
Puede utilizar una biblioteca de terceros (plug / disclaimer - He trabajado en este producto):
Como todos los demás dicen, no se puede desde una simple forma programática.
Considera estos ejemplos:
Presidente "George Herbert Walker Bush" (Primer Medio Medio)
Asesino presidencial "John Wilkes Booth" (Primer medio último)
Guitarrista "Eddie Van Halen" (Primer Último Último)
Y su madre probablemente lo llame Edward Lodewijk Van Halen (Primer medio último)
El famoso náufrago "Mary Ann Summers" (First First Last)
El presidente del Partido Republicano de Nuevo México, "Fernando C de Baca" (Primero, el último, último)
El mayor problema que encontré al hacer esto fue casos como "Bob R. Smith, Jr.". El algoritmo que utilicé está publicado en http://www.blackbeltcoder.com/Articles/strings/splitting-a-name-into-first-and-last-names . Mi código está en C # pero podría portarlo si debe tenerlo en SQL.
Es difícil responder sin saber cómo se formatea el "nombre completo".
Podría ser "Apellido, Nombre intermedio del nombre" o "Apellido del segundo nombre", etc.
Básicamente, deberás usar la función SUBSTRING
SUBSTRING ( expression , start , length )
Y probablemente la función CHARINDEX
CHARINDEX (substr, expression)
Para averiguar el inicio y la duración de cada parte que desea extraer.
Así que digamos que el formato es "Nombre Apellido" que podría (no probado ... pero debería estar cerca):
SELECT
SUBSTR(fullname, 1, CHARINDEX('' '', fullname) - 1) AS FirstName,
SUBSTR(fullname, CHARINDEX('' '', fullname) + 1, len(fullname)) AS LastName
FROM YourTable
Esto funcionará en mayúscula y minúscula es 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
Haría esto como un proceso iterativo.
1) Vuelca la tabla a un archivo plano para trabajar.
2) Escribe un programa simple para dividir tus nombres usando un espacio como separador donde el primer token es token, si hay 3 token, el token 2 es el segundo nombre y el token 3 es el último. Si hay 2 fichas, el segundo token es el apellido. (Perl, Java o C / C ++, el lenguaje no importa)
3) Eyeball los resultados. Busque los nombres que no se ajustan a esta regla.
4) Usando ese ejemplo, crea una nueva regla para manejar esa excepción ...
5) Enjuague y repita
Eventualmente obtendrás un programa que arregla todos tus datos.
Invierta el problema, agregue columnas para contener las piezas individuales y combínelas para obtener el nombre completo.
La razón por la cual esta será la mejor respuesta es que no hay una forma garantizada de descubrir que una persona se ha registrado como su primer nombre, y cuál es su segundo nombre.
Por ejemplo, ¿cómo separarías esto?
Jan Olav Olsen Heggelien
Esto, aunque ficticio, es un nombre legal en Noruega, y podría, pero no debería ser, dividirse así:
First name: Jan Olav
Middle name: Olsen
Last name: Heggelien
o así:
First name: Jan Olav
Last name: Olsen Heggelien
o así:
First name: Jan
Middle name: Olav
Last name: Olsen Heggelien
Me imagino que se pueden encontrar ocurrencias similares en la mayoría de los idiomas.
Por lo tanto, en lugar de tratar de interpretar los datos que no tienen suficiente información para hacerlo bien, almacene la interpretación correcta y combínelos para obtener el nombre completo.
No estoy seguro acerca de SQL Server, pero en Postgres puedes hacer algo como esto:
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
Las expresiones regex probablemente podrían ser un poco más concisas; Pero usted consigue el punto. Por cierto, esto no funciona para las personas que tienen dos nombres dobles (en los Países Bajos tenemos esto mucho ''Jan van der Ploeg'') así que sería muy cuidadoso con los resultados.
Para obtener una solución basada en SQL CLR gratuita, asegúrese de consultar SqlName de Ambient Concepts, que puede ser una gran ayuda para analizar nombres a nivel de base de datos.
Por supuesto, todos entendemos que no hay una manera perfecta de resolver este problema, pero algunas soluciones pueden llevarlo más lejos que otros.
En particular, es bastante fácil ir más allá de los simples divisores de espacio en blanco si solo tiene algunas listas de prefijos comunes (Mr, Dr, Mrs, etc.), infijos (von, de, del, etc.), sufijos (Jr, III , Sr, etc.) y así sucesivamente. También es útil si tiene algunas listas de nombres comunes (en varios idiomas / culturas, si sus nombres son diversos) para que pueda adivinar si una palabra en el medio es probable que sea parte del apellido o no.
BibTeX también implementa algunas heurísticas que te llevan a una parte del camino; están encapsulados en el Text::BibTeX::Name
perl. Aquí hay una muestra de código rápido que hace un trabajo razonable.
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";
Si intentas analizar un nombre humano en PHP, recomiendo el script nameparse.php de Keith Beckman .
Copie en caso de que el sitio se caiga:
<?
/*
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;
}
?>
Sujeto a las advertencias que ya se han planteado con respecto a los espacios en los nombres y otras anomalías, el siguiente código, al menos, manejará el 98% de los nombres. (Nota: SQL desordenado porque no tengo una opción de expresión regular en la base de datos que uso).
** Advertencia: sigue un desordenado 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
El código funciona creando una tabla temporal (nombre_pars) y tokenizando el nombre completo por espacios. Los nombres que terminan con valores en name3 o name4 no son conformes y deberán tratarse de manera diferente.
Una forma alternativa simple es usar 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
Una vez hice una expresión regular de 500 caracteres para analizar primero, último y segundo nombre de una cadena arbitraria. Incluso con esa expresión regular de bocina, solo obtuvo un 97% de precisión debido a la inconsistencia completa de la entrada. Aún así, mejor que nada.
Verifique esta consulta en Athena para una cadena separada de un solo espacio (p. Ej., Primer nombre y combinación de segundo nombre):
SELECT name, REVERSE( SUBSTR( REVERSE(name), 1, STRPOS(REVERSE(name), '' '') ) ) AS middle_name FROM name_table
Si espera tener dos o más espacios, puede extender fácilmente la consulta anterior.