Разделить значение из одного поля на два


125

У меня есть поле таблицы, membernameкоторое содержит как фамилии, так и имена пользователей. Можно ли разделить эти в 2 -х полей memberfirst, memberlast?

Все записи имеют формат «Имя Фамилия» (без кавычек и пробелов между ними).


6
«Все записи имеют формат« Имя Фамилия »(без кавычек и пробела между ними)». ... чудесно ... Пожалуйста, пожалуйста , не забывайте о людях , как я при принятии решений в базе данных. Слишком часто я получаю сайты, которые говорят мне, что моя фамилия содержит недопустимый (sic) символ ... :(
Stijn de Witt

@StijndeWitt В целом вы правы, однако похоже, что эта база данных не содержит вашего имени, по крайней мере, в официальной форме. В моей стране фамилии пишутся первыми, поэтому я тоже должен быть "размечен" в этой таблице данных. Просто посмотри на это ->
Давид Хорват

Ответы:


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;

Отличное решение этой проблемы!
Бергкамп

все же вы не можете использовать IN как «массив значений» из этой операции разделения?
Мигель

3
LENGTHБезопасно ли ваше использование многобайтовых файлов ? "LENGTH (str): возвращает длину строки str, измеренную в байтах. Многобайтовый символ считается как несколько байтов. Это означает, что для строки, содержащей пять 2-байтовых символов, LENGTH () возвращает 10, тогда как CHAR_LENGTH () возвращает 5.»
Erk

Это не будет работать должным образом при работе с многобайтовыми символами / utf8, как упоминал @Erk. Только простое решение с двумя операторами SUBSTRING_INDEX работает с utf8 / multibyte
Майкл

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.
  • Значения имени члена, которые имеют несколько пробелов : он добавит все, что находится перед первым пробелом, в memberfirst, а остаток (включая дополнительные пробелы) в memberlast.

Версия ОБНОВЛЕНИЯ будет:

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
    );

Также полезно было бы увидеть, как обрезать только последнее слово для фамилии и все непоследние для имени, например: Мэри А. Смит, это типы, с которыми я должен иметь дело с этим в старой таблице db фикс. Я посмотрю, смогу ли я понять это и опубликовать результат, если нет, если вы тоже можете опубликовать этот вариант, который сделает ваш ответ полным.
Lizardx

как мы можем преобразовать его в целое число, поскольку имя члена - varchar .. пусть memberfirst имеет тип int. Будет ли работать, если я использую cast () напрямую?
infinitywarior

Вы, сэр, заслужили медаль.
rpajaziti

23

Кажется, что существующие ответы слишком сложны или не являются строгим ответом на конкретный вопрос.

Думаю, простой ответ - следующий запрос:

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

Я думаю, что в данной конкретной ситуации нет необходимости иметь дело с именами, состоящими из более чем двух слов. Если вы хотите сделать это правильно, разделение может быть очень сложным или даже невозможным в некоторых случаях:

  • Иоганн Себастьян Бах
  • Иоганн Вольфганг фон Гете
  • Эдгар Аллан По
  • Якоб Людвиг Феликс Мендельсон-Бартольди
  • Петефи Шандор
  • 黒 澤 за товар описание

В правильно спроектированной базе данных человеческие имена должны храниться как по частям, так и целиком. Конечно, это не всегда возможно.


20

Если ваш план должен сделать это как часть запроса, пожалуйста , не делать (а) . Серьезно, это убийца производительности. Могут быть ситуации, когда вас не заботит производительность (например, одноразовые задания по миграции для разделения полей, позволяющие повысить производительность в будущем), но, если вы делаете это регулярно для чего-либо, кроме базы данных микки-мауса, вы зря тратим ресурсы.

Если вы когда-нибудь обнаружите, что каким-то образом обрабатываете только часть столбца, ваша БД имеет изъян. Он вполне может нормально работать с домашней адресной книгой или приложением рецептов или с любой из множества других небольших баз данных, но он не будет масштабироваться до «реальных» систем.

Храните компоненты имени в отдельных столбцах. Почти всегда гораздо быстрее объединить столбцы с помощью простой конкатенации (когда вам нужно полное имя), чем разделить их с помощью поиска по символам.

Если по какой-либо причине вы не можете разделить поле, по крайней мере, добавьте дополнительные столбцы и используйте триггер вставки / обновления для их заполнения. Хотя это и не 3NF, это гарантирует, что данные по-прежнему согласованы, и значительно ускорит ваши запросы. Вы также можете убедиться, что дополнительные столбцы имеют нижний регистр (и индексируются, если вы выполняете поиск по ним) одновременно, чтобы вам не приходилось возиться с проблемами с регистром.

И, если вы даже не можете добавить столбцы и триггеры, имейте в виду (и сообщите своему клиенту, если это для клиента), что он не масштабируется.


(a) Конечно, если вы намерены использовать этот запрос для исправления схемы, чтобы имена помещались в отдельные столбцы в таблице, а не в запросе, я бы счел это допустимым использованием. Но я повторяю, делать это в запросе - не самая лучшая идея.


4
Иногда вам нужно это делать. Fe Мне это нужно в скрипте миграции, поэтому меня не волнует производительность.
Матье Наполи

@dfmiller, да, ответил, поэтому мой аргументированный и подробный ответ и спасибо за проявленный интерес. Если у вас есть конкретная проблема с тем, что я написал, укажите на это, и я посмотрю, можно ли это исправить. Ваш текущий комментарий практически бесполезен для улучшения ситуации, если это действительно было вашим намерением. Или, может быть, вам просто нравится разбрасывать случайные комментарии в сети, это сложно сказать :-) Я поддерживаю ответ, конечно, доступ к подколонкам не масштабируется и почти всегда является плохой идеей, если только он не используется для целей фактически исправление доступа к подколонкам.
paxdiablo

3
Вопрос в том, как разделить один столбец на 2, а затем вы ответите «Не делайте этого», а затем продолжите объяснять, почему их следует разделить. Ваш первый абзац звучит так, как будто вы аргументируете или сохраняете их как одну колонку, но другие абзацы говорят об обратном.
dfmiller

@dfmiller, возможно, я неправильно понял вопрос, сейчас я не уверен, должно ли быть разделение в запросе или в таблице. Я уточнил ответ, чтобы, надеюсь, было понятнее.
paxdiablo

Намного лучше. Я никогда не рассматривал использование запроса выбора, кроме как для обновления базы данных. Это было бы ужасной идеей.
dfmiller

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


3

Единственный случай, когда вам может понадобиться такая функция, - это запрос UPDATE, который изменит вашу таблицу, чтобы сохранить имя и фамилию в отдельных полях.

Дизайн базы данных должен соответствовать определенным правилам, и нормализация базы данных является одним из наиболее важных.


Излишний комментарий, поскольку это именно то, о чем просил плакат; также неточно, так как в миллион раз вам может потребоваться разделить строку для лучшей нормализации. Не знаю, почему и как за это проголосовали.
daticon

Использование индексов в разделенных полях так же невозможно, как превратить MySQL в мульчер листьев, но это не помешает людям спросить об этом. Хороший ответ - база данных ДОЛЖНА отражать данные, а не характеристики мульчера для листьев.
HoldOffHunger

2

У меня был столбец, в котором имя и фамилия были в одном столбце. Имя и фамилия разделялись запятой. Код ниже работал. НЕТ проверки / исправления ошибок. Просто тупой раскол. Использовал phpMyAdmin для выполнения инструкции SQL.

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

13.2.10 Синтаксис UPDATE


1

Это берет smhg отсюда и curt из последнего индекса данной подстроки в MySQL и объединяет их. Это для mysql, все, что мне нужно, это получить приличное разделение имени на first_name last_name с фамилией одним словом, первым именем перед этим единственным словом, где имя может быть нулевым, 1 словом, 2 словами или более 2 слов. Т.е.: Null; Мэри; Мэри Смит; Мэри А. Смит; Мэри Сью Эллен Смит;

Итак, если name состоит из одного слова или null, last_name имеет значение null. Если name> 1 слова, last_name - это последнее слово, а first_name - все слова перед последним словом.

Обратите внимание, что я уже обрезал такие вещи, как Джо Смит младший; Джо Смит эсквайр. и так далее, вручную, что, конечно, было болезненно, но для этого было достаточно мало, поэтому вы хотите убедиться, что действительно посмотрите на данные в поле имени, прежде чем решить, какой метод использовать.

Обратите внимание, что это также обрезает результат, поэтому вы не останетесь без пробелов перед или после имен.

Я просто отправляю это для других, которые могут найти в 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 на first_name и last_name, когда все данные поступали в поле first_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 не работает. В разделе 12.5 есть предложения сообщества в комментариях для этой функции.
DRaehal 02
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.