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


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. "
Ерк

Це не спрацює належним чином при роботі з багатобайтовими / utf8 символами, як згадував @Erk. Тільки просте рішення з двома операторами SUBSTRING_INDEX працює з utf8 / multibyte
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`;

Цей підхід також забезпечує:

  • значення імені члена без пробілу : це додасть цілу рядок до 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 виправити. Я побачу, чи зможу це зрозуміти та опублікувати результат, якщо ні, якщо ви можете також розмістити цей варіант, який би доповнив вашу відповідь.
Лізардкс

як ми можемо привласнити його до цілого числа, оскільки ім'я члена варчар .. нехай memberfirst має тип int. Чи буде це працювати, якщо я безпосередньо використовую cast ()?
infinitywarior

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

23

Здається, що відповіді над складною чи не суворою відповіддю на конкретне питання.

Я думаю, проста відповідь - це наступний запит:

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

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

  • Йоганн Себастьян Бах
  • Йоганн Вольфганг фон Гете
  • Едгар Аллан По
  • Якоб Людвіг Фелікс Мендельсон-Бартольді
  • Petőfi Sándor
  • 澤黒 за товар опис

У правильно розробленій базі даних імена людини повинні зберігатися як у частинах, так і в цілому. Звичайно, це не завжди можливо.


20

Якщо ваш план полягає у виконанні цього запиту, будь ласка , не робіть цього (a) . Серйозно, це вбивця продуктивності. Можливо, трапляються ситуації, коли вам не важлива ефективність (наприклад, разові міграційні завдання для розділення полів, що дозволяють покращити ефективність у майбутньому), але якщо ви робите це регулярно для чогось іншого, крім бази даних «Міккі-миша», витрачаєш ресурси.

Якщо вам коли-небудь доведеться обробляти лише частину стовпця якимось чином, ваш дизайн БД є помилковим. Це може добре працювати з домашньою адресною книгою чи програмою рецептів або будь-якою з безлічі інших невеликих баз даних, але це не буде масштабуватися до "реальних" систем.

Зберігайте компоненти імені в окремих стовпцях. Майже незмінно набагато швидше з'єднувати стовпці разом з простим конкатенацією (коли вам потрібно повне ім’я), ніж це розділити їх на розріз пошуку символів.

Якщо з якоїсь причини ви не можете розділити поле, принаймні поставте в додаткові стовпці та використовуйте тригер вставлення / оновлення, щоб заповнити їх. Хоча це не 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

4

У MySQL працює цей параметр:

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

за відпочинковість струни на другому полі
М.Фараз

3

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

Дизайн бази даних повинен відповідати певним правилам, а нормалізація бази даних - одна з найважливіших


Необхідний коментар, оскільки саме про це попросив плакат; також неточні, оскільки в мільйон разів вам може знадобитися розділити рядок для найкращої нормалізації. Не впевнені, чому або як це коли-небудь проголосували.
датикон

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

2

У мене була стовпець, де і ім’я, і ім’я, і прізвище були в одній колоні Ім'я та прізвище були розділені комою. Код нижче працював. Немає перевірки / виправлення помилок. Просто німий розкол. Використовується phpMyAdmin для виконання оператора SQL.

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

13.2.10 ОНОВЛЕННЯ Синтаксис


1

Це приймає smhg звідси і curt з Last indeks даної підрядки в MySQL і поєднує їх. Це для mysql, все, що мені потрібно було, щоб отримати гідний розкол імені до прізвища прізвище прізвище з прізвищем одне слово, ім'я все перед цим єдиним словом, де ім'я може бути нульовим, 1 слово, 2 слова або більше 2 слів. Т.е.: Нуль; Мері; Мері Сміт; Мері А. Сміт; Мері Сью Еллен Сміт;

Отже, якщо ім'я - це одне слово або null, прізвище - null. Якщо ім'я> 1 слово, прізвище - це останнє слово, а ім’я - усе ім'я перед останнім словом.

Зауважте, що я вже обрізав такі речі, як Джо Сміт-молодший; Джо Сміт Еск. і так далі, вручну, що було болісно, ​​звичайно, але це було досить мало, щоб ви хотіли переконатися, що дійсно перегляньте дані в полі імен, перш ніж вирішити, який метод використовувати.

Зауважте, що це також зменшує результат, тому ви не закінчите пробілами перед іменами або після них.

Я просто розміщую це для інших, хто може google їхній шлях тут шукаю те, що мені потрібно. Це працює, звичайно, тестуйте його за допомогою вибору спочатку.

Це разова річ, тому я не дбаю про ефективність.

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

Метод, який я використовував, щоб розділити ім'я на ім'я та прізвище, коли дані надійшли у поле ім'я. Це додасть лише останнє слово в поле прізвища, тож "Джон Філліпс Суса" буде "Джон Філліпс" ім'ям та "Суса" прізвищем. Це також дозволяє уникнути перезапису записів, які вже були виправлені.

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.