SQL:从全名字段解析第一个、中间个和最后一个名称
如何使用SQL从全名字段中解析出第一个、中间个和最后一个名称 我需要尝试匹配那些与全名不直接匹配的名字。我希望能够采取全名字段,并将其分为第一名,中间名和姓氏 数据不包括任何前缀或后缀。中间名是可选的。数据的格式为“第一个中间最后一个”SQL:从全名字段解析第一个、中间个和最后一个名称,sql,sql-server,tsql,parsing,Sql,Sql Server,Tsql,Parsing,如何使用SQL从全名字段中解析出第一个、中间个和最后一个名称 我需要尝试匹配那些与全名不直接匹配的名字。我希望能够采取全名字段,并将其分为第一名,中间名和姓氏 数据不包括任何前缀或后缀。中间名是可选的。数据的格式为“第一个中间最后一个” 我对一些实用的解决方案感兴趣,这些解决方案可以让我在90%的过程中达到目标。如前所述,这是一个复杂的问题,因此我将单独处理特殊情况。除非您有非常、非常好的数据,否则这是一个非常重要的挑战。一种简单的方法是在空白处标记,并假设三个标记的结果是[first,midd
我对一些实用的解决方案感兴趣,这些解决方案可以让我在90%的过程中达到目标。如前所述,这是一个复杂的问题,因此我将单独处理特殊情况。除非您有非常、非常好的数据,否则这是一个非常重要的挑战。一种简单的方法是在空白处标记,并假设三个标记的结果是[first,middle,last],两个标记的结果是[first,last],但您必须处理多个单词的姓氏(例如“Van Buren”)和多个中间名。您确定完整的法定名称始终包括first、middle和last吗?我知道有些人只有一个法定全名,老实说,我不知道这是他们的名字还是姓。:-)我还认识一些人,他们的法定名称中有不止一个首名,但没有中间名。有些人有多个中间名 还有名字的顺序,在完整的法定名称中。据我所知,在一些亚洲文化中,姓氏在法律全称中排在第一位 更实用的是,您可以将全名拆分为空白,并将第一个标记作为名字,最后一个标记(或只有一个名称的情况下的唯一标记)作为姓氏。尽管这假设顺序总是相同的
我推荐Expresso用于学习/构建/测试正则表达式 不知道“全名”的格式很难回答 它可以是“姓、名、中名”或“名、中名、姓”等 基本上,您必须使用子字符串函数
SUBSTRING ( expression , start , length )
可能还有CHARINDEX函数
CHARINDEX (substr, expression)
计算要提取的每个零件的起点和长度
假设格式为“First Name Last Name”,您可以(未测试..但应该接近):
扭转这个问题,添加列来保存各个片段,并将它们组合起来以获得完整的名称 这将是最佳答案的原因是,没有确定的方法可以确定一个人的名字是注册的,中间名是什么 例如,您将如何拆分此项
Jan Olav Olsen Heggelien
这虽然是虚构的,但在挪威是一个法定名称,可以但不必这样拆分:
First name: Jan Olav
Middle name: Olsen
Last name: Heggelien
First name: Jan Olav
Last name: Olsen Heggelien
First name: Jan
Middle name: Olav
Last name: Olsen Heggelien
SELECT
SUBSTRING(fullname, '(\\w+)') as firstname,
SUBSTRING(fullname, '\\w+\\s(\\w+)\\s\\w+') as middle,
COALESCE(SUBSTRING(fullname, '\\w+\\s\\w+\\s(\\w+)'), SUBSTRING(fullname, '\\w+\\s(\\w+)')) as lastname
FROM
public.person
或者,像这样:
First name: Jan Olav
Middle name: Olsen
Last name: Heggelien
First name: Jan Olav
Last name: Olsen Heggelien
First name: Jan
Middle name: Olav
Last name: Olsen Heggelien
SELECT
SUBSTRING(fullname, '(\\w+)') as firstname,
SUBSTRING(fullname, '\\w+\\s(\\w+)\\s\\w+') as middle,
COALESCE(SUBSTRING(fullname, '\\w+\\s\\w+\\s(\\w+)'), SUBSTRING(fullname, '\\w+\\s(\\w+)')) as lastname
FROM
public.person
或者,像这样:
First name: Jan Olav
Middle name: Olsen
Last name: Heggelien
First name: Jan Olav
Last name: Olsen Heggelien
First name: Jan
Middle name: Olav
Last name: Olsen Heggelien
SELECT
SUBSTRING(fullname, '(\\w+)') as firstname,
SUBSTRING(fullname, '\\w+\\s(\\w+)\\s\\w+') as middle,
COALESCE(SUBSTRING(fullname, '\\w+\\s\\w+\\s(\\w+)'), SUBSTRING(fullname, '\\w+\\s(\\w+)')) as lastname
FROM
public.person
我想在大多数语言中都可以找到类似的情况
因此,与其试图解释没有足够信息的数据,不如存储正确的解释,并结合起来获得完整名称。我不确定SQL server,但在postgres中,您可以这样做:
First name: Jan Olav
Middle name: Olsen
Last name: Heggelien
First name: Jan Olav
Last name: Olsen Heggelien
First name: Jan
Middle name: Olav
Last name: Olsen Heggelien
SELECT
SUBSTRING(fullname, '(\\w+)') as firstname,
SUBSTRING(fullname, '\\w+\\s(\\w+)\\s\\w+') as middle,
COALESCE(SUBSTRING(fullname, '\\w+\\s\\w+\\s(\\w+)'), SUBSTRING(fullname, '\\w+\\s(\\w+)')) as lastname
FROM
public.person
正则表达式可能更简洁一些;但你明白了。顺便说一句,这对有两个双重名字的人不起作用(在荷兰,我们有很多“Jan van der Ploeg”),所以我会非常小心结果。就像#1所说的,这不是小事。连字符的姓氏、首字母、双名、逆名称序列和各种其他异常情况都会破坏精心设计的函数
您可以使用第三方库(插件/免责声明-我曾使用过此产品):
我会将此作为一个迭代过程 1) 将表格转储到平面文件以进行处理 2) 编写一个简单的程序,使用空格作为分隔符,其中firsts token是名字,如果有3个token,那么token2是中间名,token3是姓氏。如果有2个令牌,则第二个令牌是姓氏。(Perl、Java或C/C++,语言无关紧要) 3) 仔细观察结果。查找不符合此规则的名称 4) 使用该示例,创建一个新规则来处理该异常 5) 冲洗并重复
最终,您将得到一个修复所有数据的程序。这里是一个自包含的示例,具有易于操作的测试数据 在本例中,如果您的姓名包含三个以上的部分,那么所有“额外”内容都将放在LAST_name字段中。对于标识为“标题”的特定字符串,例如“DR”、“MRS”和“MR”,则有例外 如果缺少中间名,那么您只需要获得FIRST_name和LAST_name(中间名将为NULL) 您可以将它粉碎成一个巨大的嵌套子字符串块,但可读性很难,就像在SQL中这样做一样 编辑--处理以下特殊情况: 1-名称字段为空 2-名称字段包含前导/尾随空格 3-名称字段在名称中有>1个连续空格 4-名称字段仅包含名字 5-为便于阅读,将原始全名作为单独一列包含在最终输出中 6-将特定前缀列表作为单独的“标题”列处理
SELECT
FIRST_NAME.ORIGINAL_INPUT_DATA
,FIRST_NAME.TITLE
,FIRST_NAME.FIRST_NAME
,CASE WHEN 0 = CHARINDEX(' ',FIRST_NAME.REST_OF_NAME)
THEN NULL --no more spaces? assume rest is the last name
ELSE SUBSTRING(
FIRST_NAME.REST_OF_NAME
,1
,CHARINDEX(' ',FIRST_NAME.REST_OF_NAME)-1
)
END AS MIDDLE_NAME
,SUBSTRING(
FIRST_NAME.REST_OF_NAME
,1 + CHARINDEX(' ',FIRST_NAME.REST_OF_NAME)
,LEN(FIRST_NAME.REST_OF_NAME)
) AS LAST_NAME
FROM
(
SELECT
TITLE.TITLE
,CASE WHEN 0 = CHARINDEX(' ',TITLE.REST_OF_NAME)
THEN TITLE.REST_OF_NAME --No space? return the whole thing
ELSE SUBSTRING(
TITLE.REST_OF_NAME
,1
,CHARINDEX(' ',TITLE.REST_OF_NAME)-1
)
END AS FIRST_NAME
,CASE WHEN 0 = CHARINDEX(' ',TITLE.REST_OF_NAME)
THEN NULL --no spaces @ all? then 1st name is all we have
ELSE SUBSTRING(
TITLE.REST_OF_NAME
,CHARINDEX(' ',TITLE.REST_OF_NAME)+1
,LEN(TITLE.REST_OF_NAME)
)
END AS REST_OF_NAME
,TITLE.ORIGINAL_INPUT_DATA
FROM
(
SELECT
--if the first three characters are in this list,
--then pull it as a "title". otherwise return NULL for title.
CASE WHEN SUBSTRING(TEST_DATA.FULL_NAME,1,3) IN ('MR ','MS ','DR ','MRS')
THEN LTRIM(RTRIM(SUBSTRING(TEST_DATA.FULL_NAME,1,3)))
ELSE NULL
END AS TITLE
--if you change the list, don't forget to change it here, too.
--so much for the DRY prinicple...
,CASE WHEN SUBSTRING(TEST_DATA.FULL_NAME,1,3) IN ('MR ','MS ','DR ','MRS')
THEN LTRIM(RTRIM(SUBSTRING(TEST_DATA.FULL_NAME,4,LEN(TEST_DATA.FULL_NAME))))
ELSE LTRIM(RTRIM(TEST_DATA.FULL_NAME))
END AS REST_OF_NAME
,TEST_DATA.ORIGINAL_INPUT_DATA
FROM
(
SELECT
--trim leading & trailing spaces before trying to process
--disallow extra spaces *within* the name
REPLACE(REPLACE(LTRIM(RTRIM(FULL_NAME)),' ',' '),' ',' ') AS FULL_NAME
,FULL_NAME AS ORIGINAL_INPUT_DATA
FROM
(
--if you use this, then replace the following
--block with your actual table
SELECT 'GEORGE W BUSH' AS FULL_NAME
UNION SELECT 'SUSAN B ANTHONY' AS FULL_NAME
UNION SELECT 'ALEXANDER HAMILTON' AS FULL_NAME
UNION SELECT 'OSAMA BIN LADEN JR' AS FULL_NAME
UNION SELECT 'MARTIN J VAN BUREN SENIOR III' AS FULL_NAME
UNION SELECT 'TOMMY' AS FULL_NAME
UNION SELECT 'BILLY' AS FULL_NAME
UNION SELECT NULL AS FULL_NAME
UNION SELECT ' ' AS FULL_NAME
UNION SELECT ' JOHN JACOB SMITH' AS FULL_NAME
UNION SELECT ' DR SANJAY GUPTA' AS FULL_NAME
UNION SELECT 'DR JOHN S HOPKINS' AS FULL_NAME
UNION SELECT ' MRS SUSAN ADAMS' AS FULL_NAME
UNION SELECT ' MS AUGUSTA ADA KING ' AS FULL_NAME
) RAW_DATA
) TEST_DATA
) TITLE
) FIRST_NAME
我曾经制作了一个500个字符的正则表达式来解析任意字符串中的名字、姓氏和中间名。即使使用了那个响亮的正则表达式,由于输入完全不一致,它的准确率也只有97%左右。尽管如此,总比什么都没有好。根据已经提出的关于名称中空格和其他异常的警告,下面的代码将至少处理98%的错误