Dividir valor de um campo para dois


125

Eu tenho um campo de tabela membernameque contém o sobrenome e o nome dos usuários. É possível dividir aqueles em 2 campos memberfirst, memberlast?

Todos os registros têm este formato "Nome Sobrenome" (sem aspas e um espaço no meio).


6
"Todos os registros têm este formato" Nome Sobrenome "(sem aspas e um espaço no meio)." ... milagrosamente ... Por favor, por favor , não se esqueça de pessoas como eu ao tomar decisões no banco de dados. Muitas vezes eu obter sites dizendo-me meu apelido contém um ilegal caractere (sic) ... :(
Stijn de Witt

@StijndeWitt Você está certo em geral, no entanto, parece que esse banco de dados não contém seu nome, pelo menos não em sua forma oficial. No meu país, os sobrenomes são escritos primeiro, então eu também deveria ser "discriminado" nesta tabela de dados. Basta ver isso ->
Dávid Horváth 07/02

Respostas:


226

Infelizmente, o MySQL não possui uma função de string dividida. No entanto, você pode criar uma função definida pelo usuário para isso, como a descrita no seguinte artigo:

Com essa função:

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 ;

você poderia criar sua consulta da seguinte maneira:

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

Se você preferir não usar uma função definida pelo usuário e não considerar a consulta um pouco mais detalhada, também poderá fazer o seguinte:

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

Ótima solução para esse problema!
Bergkamp

ainda assim você não pode usar o IN como uma "matriz de valores" dessa operação de divisão?
Miguel Miguel

3
O uso de LENGTHmultibyte é seguro? "LENGTH (str): retorna o comprimento da string str, medida em bytes. Um caractere multibyte conta como vários bytes. Isso significa que, para uma string que contém cinco caracteres de 2 bytes, LENGTH () retorna 10, enquanto CHAR_LENGTH () retorna 5. "
Erk

Isso não funcionará corretamente ao lidar com caracteres multibyte / utf8, como o @Erk mencionou. Apenas a solução simples com as duas declarações SUBSTRING_INDEX trabalha com utf8 / multibyte
Michael

LENGTH (), LOCATE () ou qualquer coisa que dependa de uma contagem de posições falhará com caracteres multibyte.
Michael Michael

68

Variante SELECT (não criando uma função definida pelo usuário):

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

Essa abordagem também cuida de:

  • valores membername sem um espaço : ele adicionará a string inteira a memberfirst e configurará memberlast como NULL.
  • valores membername que possuem vários espaços : ele adicionará tudo antes do primeiro espaço ao memberfirst e o restante (incluindo espaços adicionais) ao memberlast.

A versão UPDATE seria:

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

Também seria útil ver como cortar apenas a última palavra do sobrenome e todas as não-últimas do primeiro nome, por exemplo: Mary A. Smith, que são os tipos que tenho que lidar com isso em uma tabela db antiga consertar. Vou ver se consigo descobrir e publicar o resultado, se não, se você também pode postar essa opção, o que tornaria sua resposta completa.
Lizardx

como podemos convertê-lo para inteiro, uma vez que membername é varchar .. seja memberfirst do tipo int. Funcionará se eu usar diretamente cast ()?
Infinitywarior

Você merece uma medalha.
rpajaziti

23

Parece que as respostas existentes são mais complicadas ou não são uma resposta estrita à pergunta em particular.

Eu acho que a resposta simples é a seguinte consulta:

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

Eu acho que não é necessário lidar com nomes com mais de duas palavras nessa situação específica. Se você deseja fazer isso corretamente, a divisão pode ser muito difícil ou até impossível em alguns casos:

  • Johann Sebastian Bach
  • Johann Wolfgang von Goethe
  • Edgar Allan Poe
  • Ludobig, Jakob Mendelssohn-Bartholdy, Felix
  • Petőfi Sándor
  • 澤黒

Em um banco de dados projetado adequadamente, os nomes humanos devem ser armazenados em partes e no todo. Isso nem sempre é possível, é claro.


20

Se o seu plano é fazer isso como parte de uma consulta, por favor, não faça isso (a) . Sério, é um matador de desempenho. Pode haver situações em que você não se preocupa com o desempenho (como tarefas de migração pontuais para dividir os campos, permitindo melhor desempenho no futuro), mas, se você estiver fazendo isso regularmente para algo que não seja um banco de dados mickey-mouse, está desperdiçando recursos.

Se você alguma vez encontrar-se ter que processar apenas parte de uma coluna de alguma forma, seu projeto DB é falho. Pode funcionar bem em um catálogo de endereços residencial ou aplicativo de receitas ou em qualquer um dos inúmeros outros bancos de dados pequenos, mas não será escalável para sistemas "reais".

Armazene os componentes do nome em colunas separadas. É quase invariavelmente muito mais rápido juntar colunas com uma concatenação simples (quando você precisa do nome completo) do que separá-las com uma pesquisa de caracteres.

Se, por algum motivo, você não puder dividir o campo, insira pelo menos as colunas extras e use um gatilho de inserção / atualização para preenchê-las. Embora não seja o 3NF, isso garantirá que os dados ainda sejam consistentes e acelerará enormemente suas consultas. Você também pode garantir que as colunas extras sejam minúsculas (e indexadas se você estiver pesquisando nelas) ao mesmo tempo, para não ter que se preocupar com problemas de maiúsculas e minúsculas.

E, se você não pode nem adicionar as colunas e os gatilhos, esteja ciente (e informe seu cliente, se for para um cliente) de que não é escalável.


(a) Obviamente, se sua intenção é usar essa consulta para corrigir o esquema, para que os nomes sejam colocados em colunas separadas na tabela, e não na consulta, considero que é um uso válido. Mas reitero que fazê-lo na consulta não é realmente uma boa ideia.


4
Às vezes, você precisa fazer isso. Por exemplo, eu preciso disso em um script de migração, para não me importar com performances.
Matthieu Napoli

@dfmiller, sim, fiz, portanto, minha resposta fundamentada e detalhada, e obrigado pelo seu interesse. Se você tiver um problema específico com algo que escrevi, aponte-o e verei se ele pode ser melhorado. Seu comentário atual é praticamente inútil para melhorar a situação, se essa foi realmente a sua intenção. Ou talvez você goste de comentar aleatoriamente na rede, é difícil dizer :-) Eu mantenho a resposta, é claro, o acesso sub-colunar não é escalável e é quase sempre uma má ideia, a menos que seja usado para fins de realmente corrigindo o acesso sub-colunar.
paxdiablo

3
A questão é como dividir a coluna única em 2 e, em seguida, você responde dizendo "Não faça isso" e, em seguida, prossegue para explicar por que elas devem ser divididas. Seu primeiro parágrafo parece que você está argumentando a favor ou mantendo-os como uma coluna, mas os outros parágrafos dizem o contrário.
dfmiller

@dfmiller, talvez eu tenha entendido mal a pergunta, agora não tenho certeza se a separação deve ser feita na consulta ou na tabela. Esclarei a resposta para, espero, torná-la mais clara.
paxdiablo 12/09

Muito melhor. Eu nunca considerei usar uma consulta de seleção, exceto para atualizar o banco de dados. Seria uma péssima ideia.
Dfmiller 12/09/14

7

usa isto

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

Isso capturará a primeira e a última substring delimitada por espaço do campo, o que não funciona em todas as circunstâncias. Por exemplo, se o campo de nome for "Lilly von Schtupp", você receberá 'Lilly', 'Schtupp' como o primeiro nome, sobrenome.
John Franklin

5

Não exatamente respondendo à pergunta, mas diante do mesmo problema, acabei fazendo o seguinte:

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

No MySQL, está funcionando esta opção:

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

para tirar resto da string em segundo campo
M. Faraz

3

O único caso em que você pode querer essa função é uma consulta UPDATE que alterará sua tabela para armazenar Nome e Sobrenome em campos separados.

O design do banco de dados deve seguir certas regras e a Normalização do Banco de Dados está entre as mais importantes


Comentário desnecessário, pois é exatamente isso que o pôster solicitou; também imprecisa, pois há um milhão de vezes que você pode precisar dividir uma sequência para melhor normalização. Não sei por que ou como isso foi votado.
Daticon 16/05/19

Usar índices em campos divididos é quase tão impossível quanto transformar o MySQL em um mulcher, mas isso não impedirá as pessoas de perguntar sobre isso. Boa resposta - o banco de dados DEVE refletir os dados, e não as especificações do mulcher de folhas.
HoldOffHunger

2

Eu tinha uma coluna onde o nome e o sobrenome estavam em uma coluna. O nome e o sobrenome foram separados por vírgula. O código abaixo funcionou. Não há verificação / correção de erro. Apenas uma divisão idiota. Utilizou o phpMyAdmin para executar a instrução SQL.

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

13.2.10 Sintaxe UPDATE


1

Isso pega smhg daqui e curt's from Last index de uma determinada substring no MySQL e os combina. Isso é para o mysql, tudo que eu precisava era obter uma divisão decente do nome para first_name last_name com o sobrenome uma única palavra, o primeiro nome tudo antes dessa palavra, onde o nome poderia ser nulo, 1 palavra, 2 palavras ou mais de 2 palavras. Ou seja: nulo; Maria; Mary Smith; Mary A. Smith; Mary Sue Ellen Smith;

Portanto, se nome for uma palavra ou nulo, last_name será nulo. Se o nome for> 1 palavra, last_name será a última palavra e first_name todas as palavras antes da última palavra.

Note que eu já aparei coisas como Joe Smith Jr.; Joe Smith Esq. e assim por diante, manualmente, o que foi doloroso, é claro, mas era pequeno o suficiente para fazer isso, então você deve ter certeza de observar realmente os dados no campo de nome antes de decidir qual método usar.

Observe que isso também apara o resultado, para que você não termine com espaços na frente ou após os nomes.

Só estou postando isso para outras pessoas que podem procurar no Google aqui o que eu precisava. Obviamente, isso funciona testando-o primeiro com o select.

É uma coisa única, então não me importo com eficiência.

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

O método que eu usei para dividir first_name em first_name e last_name quando os dados chegaram todos no campo first_name. Isso colocará apenas a última palavra no campo sobrenome; portanto, "john phillips sousa" será o nome "john phillips" e o sobrenome "sousa". Também evita a substituição de todos os registros que já foram corrigidos.

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

O mysql 5.4 fornece uma função de divisão nativa:

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

1
Você pode fornecer um link para a documentação. Uma pesquisa no dev.mysql.com é finalizada. A Seção 12.5 tem uma sugestão da comunidade nos comentários para esta função.
DRaehal
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.