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


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.