한 필드에서 두 필드로 값 나누기


125

membername성 및 이름이 모두 포함 된 테이블 필드 가 있습니다. 그것은 2 개에 그 분할 할 수 있는가 memberfirst, memberlast?

모든 레코드의 형식은 "이름 성"(따옴표없이 공백 사이)입니다.


6
"모든 레코드의 형식은"이름 성 "(따옴표없이 공백 사이)입니다." ... 기적적으로 ... 제발, 제발 , 데이터베이스 결정을 할 때 나와 같은 사람들을 잊지 마십시오. 너무 자주 웹 사이트에 내성에 불법적 인 문자 가 포함되어 있다고 알려주는 경우가 있습니다 ... :(
Stijn de Witt

@StijndeWitt 귀하는 일반적으로 옳습니다. 그러나이 데이터베이스에는 최소한 공식적인 형식이 아닌 귀하의 이름이 포함되어 있지 않은 것 같습니다. 우리나라에서는 성이 먼저 쓰여 있으므로이 데이터 표에서 "차별화"되어야합니다. 이것을보십시오->
Dávid Horváth

답변:


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

여전히 그 분할 연산에서 IN을 "값 배열"로 사용할 수 없습니까?
Miguel

3
LENGTH멀티 바이트를 안전하게 사용할 수 있습니까? "LENGTH (str) : 바이트 단위로 측정 된 문자열 str의 길이를 리턴합니다. 멀티 바이트 문자는 다중 바이트로 계산됩니다. 이는 5 개의 2 바이트 문자를 포함하는 문자열의 경우 LENGTH ()는 10을 리턴하지만 CHAR_LENGTH ()는 리턴합니다 5. "
Erk

@Erk에서 언급했듯이 멀티 바이트 / utf8 문자를 처리 할 때는 제대로 작동하지 않습니다. 두 개의 SUBSTRING_INDEX 문이있는 간단한 솔루션 만 utf8 / 멀티 바이트와 함께 작동합니다
Michael

LENGTH (), LOCATE () 또는 위치 수에 의존하는 것은 멀티 바이트 문자로 실패합니다.
Michael

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

또한 성의 마지막 단어와 성의 마지막 비 단어를 잘라내는 방법을 보는 것이 유용합니다. 예를 들어 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)를 . 진심으로, 성능을 저하시키는 요인입니다. 성능에 신경 쓰지 않는 상황이있을 수 있습니다 (예 : 미래에 더 나은 성능을 제공하기 위해 필드를 분할하기위한 일회성 마이그레이션 작업). 그러나 미키 마우스 데이터베이스 이외의 다른 작업에 대해 정기적으로이 작업을 수행하는 경우 자원을 낭비하고 있습니다.

당신이 경우 지금까지 자신이 어떤 방식의 열 일부만을 처리하는 것을 발견, 당신의 DB 설계는 결함이있다. 홈 주소록이나 레시피 응용 프로그램 또는 수많은 다른 작은 데이터베이스에서는 제대로 작동하지만 "실제"시스템으로 확장 할 수는 없습니다.

이름의 구성 요소를 별도의 열에 저장하십시오. 문자 검색으로 열을 나누는 것보다 간단한 연결 (전체 이름이 필요할 때)로 열을 조인하는 것이 거의 항상 빠릅니다.

어떤 이유로 필드를 분할 할 수없는 경우 최소한 추가 열을 넣고 삽입 / 업데이트 트리거를 사용하여 필드를 채우십시오. 3NF는 아니지만 데이터의 일관성이 유지되고 쿼리 속도가 크게 향상됩니다. 또한 사례 문제를 해결하기 위해 여분의 열을 동시에 소문자로 검색하고 색인을 생성하도록 할 수도 있습니다.

또한 열과 트리거를 추가 할 수없는 경우 확장 할 수 없다는 점을 인식하고 클라이언트에게 알리십시오.


(a) 물론이 쿼리를 사용 하여 스키마 를 수정 하여 이름이 쿼리가 아닌 테이블의 별도 열에 배치 되도록하려는 경우 유효한 사용으로 간주합니다. 그러나 나는 반복하여 쿼리에서 그것을하는 것은 실제로 좋은 생각이 아닙니다.


4
때로는 그렇게해야합니다. Fe 마이그레이션 스크립트에 필요하므로 성능에 신경 쓰지 않습니다.
Matthieu Napoli

@ 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'가 표시됩니다.
John Franklin

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 쿼리입니다.

데이터베이스 디자인은 특정 규칙을 따라야하며 데이터베이스 정규화 는 가장 중요한 규칙 중 하나입니다.


이것은 포스터가 요구 한 것과 정확히 같은 의견입니다. 최상의 정규화를 위해 문자열을 분할해야하는 백만 번이 있기 때문에 정확하지 않습니다. 왜 또는 어떻게 투표했는지 확실하지 않습니다.
daticon

분할 필드에서 인덱스를 사용하는 것은 MySQL을 리프 멀 처로 만드는 것만 큼 불가능하지만 사람들이 그것에 대해 묻는 것을 막지는 않습니다. 좋은 답변-데이터베이스는 리프 멀커 사양이 아닌 데이터를 반영해야합니다.
HoldOffHunger

2

이름과 성이 모두 한 열에있는 열이있었습니다. 성은 쉼표로 구분됩니다. 아래 코드가 작동했습니다. 오류 확인 / 수정이 없습니다. 멍청한 분할. phpMyAdmin을 사용하여 SQL 문을 실행했습니다.

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

13.2.10 업데이트 구문


1

이것은 여기에서 smhg를 가져오고 MySQL에서 주어진 하위 문자열의 Last index 에서 curt를 가져 와서 결합합니다. 이것은 mysql을위한 것입니다. 필요한 이름은 first_name last_name으로 이름을 나누는 것이 었습니다. 이름은 단일 단어, 이름은 단일 단어 이전의 모든 것, 여기서 이름은 null, 1 단어, 2 단어 또는 2 단어 이상. 즉 : Null; 메리; 메리 스미스; 메리 에이 스미스; 메리 수 엘렌 스미스;

따라서 name이 한 단어이거나 null이면 last_name은 null입니다. name이> 1 워드 인 경우 last_name은 마지막 단어이며 first_name은 모든 단어를 마지막 단어 앞입니다.

나는 이미 Joe Smith Jr.와 같은 것들을 다듬 었습니다. 조 스미스 Esq. 물론 수동으로도 고통 스럽지만 그렇게하기에는 충분히 작았으므로 사용할 방법을 결정하기 전에 이름 필드의 데이터를 실제로 확인해야합니다.

이것은 또한 결과를 잘라내므로 이름 앞이나 뒤에 공백이 생기지 않습니다.

나는 여기에 내가 필요한 것을 찾고있는 사람들을 위해 이것을 게시하고 있습니다. 물론 이것은 먼저 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을 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에 대한 검색이 건조합니다. 12.5 절에는이 기능에 대한 의견에 커뮤니티 제안이 있습니다.
DRaehal
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.