将值从一个字段拆分为两个


125

我有一个表字段membername,其中包含用户的姓氏和名字。是否有可能分裂成那些2场memberfirstmemberlast

所有记录的格式均为“名字的姓氏”(不带引号,中间还有空格)。


6
“所有记录的格式均为“姓氏名”(不带引号,并且中间没有空格)。...奇迹般地...请,不要在制定数据库决策时忘记像我这样的人。我经常看到网站告诉我我的姓氏包含一个非法(sic)字符... :(
Stijn de Witt

@StijndeWitt一般来说,您是正确的,但是,该数据库似乎没有包含您的姓名,至少没有包含其正式名称。在我的国家/地区,姓氏是第一个写的,因此在此数据表中,我也应“区分”。只是看到这一点- >
戴维·霍瓦斯

Answers:


226

不幸的是,MySQL不具有分割字符串功能。但是,您可以为此创建一个用户定义的函数,例如以下文章中描述的函数

使用该功能:

DELIMITER $$

CREATE FUNCTION SPLIT_STR(
  x VARCHAR(255),
  delim VARCHAR(12),
  pos INT
)
RETURNS VARCHAR(255) DETERMINISTIC
BEGIN 
    RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
       LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
       delim, '');
END$$

DELIMITER ;

您将能够按以下方式构建查询:

SELECT SPLIT_STR(membername, ' ', 1) as memberfirst,
       SPLIT_STR(membername, ' ', 2) as memberlast
FROM   users;

如果您不想使用用户定义的函数,并且不介意查询更加冗长,则还可以执行以下操作:

SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(membername, ' ', 1), ' ', -1) as memberfirst,
       SUBSTRING_INDEX(SUBSTRING_INDEX(membername, ' ', 2), ' ', -1) as memberlast
FROM   users;

解决这个问题的好方法!
Bergkamp,2015年

仍然不能将IN用作该拆分操作的“值数组”?
Miguel

3
您使用 LENGTH多字节安全吗?“ LENGTH(str):返回字符串str的长度,以字节为单位。多字节字符计为多个字节。这意味着对于包含五个2字节字符的字符串,LENGTH()返回10,而CHAR_LENGTH()返回5.”
Erk

如@Erk所述,在处理多字节/ utf8字符时,这将无法正常工作。只有带有两个SUBSTRING_INDEX语句的简单解决方案才能与utf8 /多字节一起使用
Michael,

LENGTH(),LOCATE()或任何依赖位置计数的内容都将因多字节字符而失败。
迈克尔

68

SELECT变体(不创建用户定义的函数):

SELECT IF(
        LOCATE(' ', `membername`) > 0,
        SUBSTRING(`membername`, 1, LOCATE(' ', `membername`) - 1),
        `membername`
    ) AS memberfirst,
    IF(
        LOCATE(' ', `membername`) > 0,
        SUBSTRING(`membername`, LOCATE(' ', `membername`) + 1),
        NULL
    ) AS memberlast
FROM `user`;

此方法还应注意:

  • membername 值不带空格:它将整个字符串添加到memberfirst并将memberlast设置为NULL。
  • 具有多个空格的 membername :它将在第一个空格之前的所有内容添加到memberfirst,并将其余部分(包括其他空格)添加到memberlast。

UPDATE版本为:

UPDATE `user` SET
    `memberfirst` = IF(
        LOCATE(' ', `membername`) > 0,
        SUBSTRING(`membername`, 1, LOCATE(' ', `membername`) - 1),
        `membername`
    ),
    `memberlast` = IF(
        LOCATE(' ', `membername`) > 0,
        SUBSTRING(`membername`, LOCATE(' ', `membername`) + 1),
        NULL
    );

同样有用的是,看看如何只剪掉姓氏的姓氏,以及所有非姓氏的姓氏,例如:Mary A. Smith,这是我必须在旧的db表中处理的类型固定。我将查看是否可以找出答案并发布结果,如果不能,那么您也可以发布该选项,这将使您的答案完整。
Lizardx

由于membername是varchar,我们如何将其转换为整数。.让memberfirst为int类型。如果我直接使用cast()会起作用吗?
infinitywarior

先生,您应该得到一枚奖牌。
rpajaziti

23

似乎现有的回答过于复杂,或者不是对特定问题的严格回答。

我认为,简单的答案是以下查询:

SELECT
    SUBSTRING_INDEX(`membername`, ' ', 1) AS `memberfirst`,
    SUBSTRING_INDEX(`membername`, ' ', -1) AS `memberlast`
;

我认为在这种特殊情况下,不必处理两个以上的单词。如果您想正确执行操作,则在某些情况下拆分可能会非常困难,甚至是不可能的:

  • 约翰·塞巴斯蒂安· 巴赫
  • 约翰·沃尔夫冈·冯·歌德
  • 埃德加· 艾伦·坡
  • 雅各布·路德维希·费利克斯· 门德尔松-巴特迪
  • 佩特菲 ·
  • 泽黒:明

在一个经过适当设计的数据库中,人名应该部分存储或全部存储。当然,这并非总是可能的。


20

如果您的计划是将其作为查询的一部分要这样做(a)。认真地说,它是性能杀手。在某些情况下,您可能并不关心性能(例如,一次性迁移作业会拆分字段以在将来提供更好的性能),但是,如果您定期对除米老鼠数据库以外的任何事物执行此操作,则您会在浪费资源。

如果你曾经发现自己有只处理以某种方式一栏的一部分,你的数据库的设计是有缺陷的。在家庭地址簿或食谱应用程序或无数其他小型数据库中,它可能还可以正常工作,但无法扩展到“真实”系统。

将名称的组成部分存储在单独的列中。通过简单的串联(当需要全名时)将列连接在一起比通过字符搜索将列分开要快得多。

如果由于某种原因您无法拆分该字段,请至少放入多余的列并使用插入/更新触发器来填充它们。虽然不是3NF,但这将确保数据仍然一致,并会大大加快查询速度。您还可以确保多余的列同时是小写的(如果要搜索它们,则将它们编入索引),这样就不必摆弄大小写问题。

而且,如果您甚至无法添加列和触发器,请意识到(并且让您的客户端知道,如果是针对客户端的)它是不可扩展的。


(a)当然,如果您的意图是使用此查询来修复架构,以便将名称放置在而不是查询中的单独列中,则我认为这是一种有效的用法。但我重申,在查询中执行此操作并不是一个好主意。


4
有时,您必须这样做。如果我在迁移脚本中需要它,那么我不在乎性能。
Matthieu Napoli

@dfmiller,是的,我做到了,因此我进行了合理而详尽的回复,并感谢您的关注。如果您对我写的东西有特定的问题,请指出来,我会看看是否可以改进。如果确实是您的意图,那么您当前的评论对改善情况几乎没有用。或者,也许您就像在网上喷洒随机评论一样,很难说:-)我支持答案,当然,子列访问是不可扩展的,并且几乎总是一个坏主意,除非将其用于以下目的:实际上修复了子列访问。
paxdiablo 2014年

3
问题是如何将单列拆分为2,然后您回答“不要这样做”,然后继续解释为什么应拆分它们。您的第一段听起来好像是您赞成还是保留为一栏,而其他段则相反。
dfmiller 2014年

@dfmiller,也许我误解了这个问题,现在不确定是否要在查询或表中进行分离。我已经澄清了答案,希望可以更清楚地说明。
paxdiablo 2014年

好多了。除了更新数据库外,我从未考虑使用选择查询。那将是一个可怕的想法。
dfmiller 2014年

7

用这个

SELECT SUBSTRING_INDEX(SUBSTRING_INDEX( `membername` , ' ', 2 ),' ',1) AS b, 
SUBSTRING_INDEX(SUBSTRING_INDEX( `membername` , ' ', -1 ),' ',2) AS c FROM `users` WHERE `userid`='1'

这将从字段中获取第一个和最后一个以空格分隔的子字符串,但并非在所有情况下都有效。例如,如果名称字段是“ Lilly von Schtupp”,那么您将获得“ Lilly”,“ Schtupp”作为姓氏。
约翰·富兰克林

5

没有完全回答问题,但是面对同样的问题,我最终这样做:

UPDATE people_exit SET last_name = SUBSTRING_INDEX(fullname,' ',-1)
UPDATE people_exit SET middle_name = TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(fullname,last_name,1),' ',-2))
UPDATE people_exit SET middle_name = '' WHERE CHAR_LENGTH(middle_name)>3 
UPDATE people_exit SET first_name = SUBSTRING_INDEX(fullname,concat(middle_name,' ',last_name),1)
UPDATE people_exit SET first_name = middle_name WHERE first_name = ''
UPDATE people_exit SET middle_name = '' WHERE first_name = middle_name

4

在MySQL中,此选项有效:

SELECT Substring(nameandsurname, 1, Locate(' ', nameandsurname) - 1) AS 
       firstname, 
       Substring(nameandsurname, Locate(' ', nameandsurname) + 1)    AS lastname 
FROM   emp  

用于将其余的字符串带入第二个字段
M. Faraz

3

您可能需要这种功能的唯一情况是UPDATE查询,该查询将更改表以将Firstname和Lastname存储在单独的字段中。

数据库设计必须遵循某些规则,而数据库规范化是最重要的规则之一


不必要的评论,因为这正是发帖人所要的;也是不准确的,因为可能需要一百万次拆分字符串才能获得最佳归一化。不确定为什么或如何投票。
daticon '18年

在拆分字段上使用索引几乎与将MySQL变成叶子覆盖程序一样不可能,但这不会阻止人们对此进行询问。好的答案-数据库应该反映数据,而不是您的叶子覆盖物规格。
HoldOffHunger

2

我有一列,名字和姓氏都在一个列中。名字和姓氏之间用逗号分隔。下面的代码有效。没有错误检查/更正。只是愚蠢的分裂。使用phpMyAdmin执行SQL语句。

UPDATE tblAuthorList SET AuthorFirst = SUBSTRING_INDEX(AuthorLast,',',-1) , AuthorLast = SUBSTRING_INDEX(AuthorLast,',',1);

13.2.10 UPDATE语法


1

这需要从此处获取smhg,并需要从MySQL中给定子字符串的Last索引获取curt并将它们组合在一起。这是针对mysql的,我所需要做的就是将名字拆分成first_name last_name,将姓氏用一个单词表示,将名字放在该单词之前的所有内容,其中名称可以为null,1个单词,2个单词或超过2个字。即:无;玛丽; 玛丽·史密斯;玛丽·A·史密斯;玛丽·苏·埃伦·史密斯(Mary Sue Ellen Smith);

因此,如果名称是一个单词或为空,则last_name为空。如果name> 1个单词,last_name是最后一个单词,并且first_name所有单词都在最后一个单词之前。

注意,我已经裁掉了小乔·史密斯(Joe Smith Jr.)之类的东西。乔·史密斯Esq。等等,当然,这是很痛苦的,但是它很小,足以做到这一点,因此,在确定使用哪种方法之前,您需要确保真正查看名称字段中的数据。

请注意,这也会修剪结果,因此您不会在名称的前面或后面最后留空格。

我只是将其发布给其他可能在这里使用google搜索我所需内容的人。当然,此方法有效,请先选择select进行测试。

这是一回事,所以我不在乎效率。

SELECT TRIM( 
    IF(
        LOCATE(' ', `name`) > 0,
        LEFT(`name`, LENGTH(`name`) - LOCATE(' ', REVERSE(`name`))),
        `name`
    ) 
) AS first_name,
TRIM( 
    IF(
        LOCATE(' ', `name`) > 0,
        SUBSTRING_INDEX(`name`, ' ', -1) ,
        NULL
    ) 
) AS last_name
FROM `users`;


UPDATE `users` SET
`first_name` = TRIM( 
    IF(
        LOCATE(' ', `name`) > 0,
        LEFT(`name`, LENGTH(`name`) - LOCATE(' ', REVERSE(`name`))),
        `name`
    ) 
),
`last_name` = TRIM( 
    IF(
        LOCATE(' ', `name`) > 0,
        SUBSTRING_INDEX(`name`, ' ', -1) ,
        NULL
    ) 
);

0

当数据全部到达first_name字段中时,方法I曾经将first_name分为first_name和last_name。这只会在姓氏字段中输入姓氏,因此“ john phillips sousa”将是“ john phillips”的姓氏和“ sousa”的姓氏。它还避免覆盖任何已修复的记录。

set last_name=trim(SUBSTRING_INDEX(first_name, ' ', -1)), first_name=trim(SUBSTRING(first_name,1,length(first_name) - length(SUBSTRING_INDEX(first_name, ' ', -1)))) where list_id='$List_ID' and length(first_name)>0 and length(trim(last_name))=0

0
UPDATE `salary_generation_tbl` SET
    `modified_by` = IF(
        LOCATE('$', `other_salary_string`) > 0,
        SUBSTRING(`other_salary_string`, 1, LOCATE('$', `other_salary_string`) - 1),
        `other_salary_string`
    ),
    `other_salary` = IF(
        LOCATE('$', `other_salary_string`) > 0,
        SUBSTRING(`other_salary_string`, LOCATE('$', `other_salary_string`) + 1),
        NULL
    );

-3

mysql 5.4提供了本机拆分功能:

SPLIT_STR(<column>, '<delimiter>', <index>)

1
您能否提供文档链接。搜索dev.mysql.com变得干dry了。12.5节在此功能的注释中确实有一个社区建议。
DRaehal
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.