Divide el valor de un campo a dos


125

Tengo un campo de tabla membernameque contiene tanto el apellido como el nombre de los usuarios. ¿Es posible dividir los campos en 2 memberfirst, memberlast?

Todos los registros tienen este formato "Nombre Apellido" (sin comillas y un espacio intermedio).


66
"Todos los registros tienen este formato" Nombre Apellido "(sin comillas y un espacio intermedio)". ... milagrosamente ... Por favor, por favor , no te olvides de personas como yo al tomar decisiones en la base de datos. Con demasiada frecuencia recibo sitios web que me dicen que mi apellido contiene un carácter ilegal (sic) ... :(
Stijn de Witt

@StijndeWitt Tiene razón en general, sin embargo, parece que esta base de datos no contiene su nombre, al menos no en su forma oficial. En mi país, los apellidos se escriben primero, por lo que también debería ser "discriminado" en esta tabla de datos. Solo vea esto ->
Dávid Horváth

Respuestas:


226

Lamentablemente, MySQL no cuenta con una función de cadena dividida. Sin embargo, puede crear una función definida por el usuario para esto, como la que se describe en el siguiente artículo:

Con esa función:

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 ;

usted podría construir su consulta de la siguiente manera:

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

Si prefiere no utilizar una función definida por el usuario y no le importa que la consulta sea un poco más detallada, también puede hacer lo siguiente:

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

¡Gran solución para este problema!
Bergkamp

¿todavía no puede usar el IN como un "conjunto de valores" de esa operación dividida?
Miguel

3
¿Es LENGTHseguro su uso de multibyte? "LENGTH (str): devuelve la longitud de la cadena str, medida en bytes. Un carácter multibyte cuenta como varios bytes. Esto significa que para una cadena que contiene cinco caracteres de 2 bytes, LENGTH () devuelve 10, mientras que CHAR_LENGTH () devuelve 5. "
Erk

Esto no funcionará correctamente cuando se trate de caracteres multibyte / utf8, como se mencionó en @Erk. Solo la solución simple con las dos declaraciones SUBSTRING_INDEX funciona con utf8 / multibyte
Michael

LENGTH (), LOCATE () o cualquier cosa que se base en un recuento de posición fallará con los caracteres multibyte.
Michael

68

Variante SELECT (sin crear una función definida por el usuario):

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

Este enfoque también se ocupa de:

  • valores de nombre de miembro sin espacio : agregará toda la cadena a memberfirst y establecer memberlast a NULL.
  • valores de nombre de miembro que tienen múltiples espacios : agregará todo antes del primer espacio a memberfirst y el resto (incluidos espacios adicionales) a memberlast.

La versión ACTUALIZADA sería:

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

También sería útil ver cómo cortar solo la última palabra para el apellido y todos los no apellidos para el primer nombre, por ejemplo: Mary A. Smith, que es el tipo con el que tengo que lidiar con esto en una vieja tabla db reparar. Veré si puedo resolverlo y publicar el resultado, si no, si puedes publicar esa opción también, eso haría que tu respuesta sea completa.
Lizardx

¿Cómo podemos convertirlo en un entero ya que membername es varchar ... deje que memberfirst sea de tipo int. ¿Funcionará si uso directamente cast ()?
infinitywarior

Usted señor se merece una medalla.
rpajaziti

23

Parece que las respuestas existentes son demasiado complicadas o no una respuesta estricta a la pregunta en particular.

Creo que la respuesta simple es la siguiente consulta:

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

Creo que no es necesario tratar con nombres de más de dos palabras en esta situación particular. Si desea hacerlo correctamente, la división puede ser muy difícil o incluso imposible en algunos casos:

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

En una base de datos diseñada adecuadamente, los nombres humanos deben almacenarse tanto en partes como en su totalidad. Esto no siempre es posible, por supuesto.


20

Si su plan es hacer esto como parte de una consulta, por favor no hacer eso (a) . En serio, es un asesino de rendimiento. Puede haber situaciones en las que no le interese el rendimiento (como trabajos de migración únicos para dividir los campos que permiten un mejor rendimiento en el futuro) pero, si lo hace con regularidad para otra cosa que no sea una base de datos de mickey-mouse, Estás desperdiciando recursos.

Si alguna vez tiene que procesar solo una parte de una columna de alguna manera, su diseño de base de datos es defectuoso. Puede funcionar bien en una libreta de direcciones o una aplicación de recetas o en una miríada de otras bases de datos pequeñas, pero no será escalable a sistemas "reales".

Almacene los componentes del nombre en columnas separadas. Es casi siempre mucho más rápido unir columnas con una simple concatenación (cuando se necesita el nombre completo) que separarlas con una búsqueda de caracteres.

Si, por alguna razón, no puede dividir el campo, al menos coloque las columnas adicionales y use un activador de inserción / actualización para completarlas. Si bien no es 3NF, esto garantizará que los datos sigan siendo consistentes y acelerará enormemente sus consultas. También puede asegurarse de que las columnas adicionales estén en minúsculas (e indexadas si las está buscando) al mismo tiempo para no tener que lidiar con problemas de casos.

Y, si ni siquiera puede agregar las columnas y los desencadenantes, tenga en cuenta (y haga saber a su cliente, si es para un cliente) que no es escalable.


(a) Por supuesto, si su intención es utilizar esta consulta para corregir el esquema de modo que los nombres se coloquen en columnas separadas en la tabla en lugar de la consulta, consideraría que es un uso válido. Pero reitero, hacerlo en la consulta no es realmente una buena idea.


44
A veces, tienes que hacerlo. Fe Lo necesito en un script de migración, por lo que no me importan las actuaciones.
Matthieu Napoli

@dfmiller, sí, lo hice, de ahí mi respuesta razonada y detallada, y gracias por su interés. Si tiene un problema específico con algo que escribí, indíquelo y veré si se puede mejorar. Su comentario actual es bastante inútil para mejorar la situación, si esa fue realmente su intención. O tal vez simplemente le gusta decir comentarios al azar en la red, es difícil saber :-) Respaldo la respuesta, por supuesto, el acceso sub-columnar no es escalable y casi siempre es una mala idea, a menos que se use con el propósito de en realidad arreglando el acceso sub-columnar.
paxdiablo

3
La pregunta es cómo dividir la columna individual en 2 y luego responder diciendo "No hacer eso" y luego explicar por qué deberían dividirse. Su primer párrafo suena como si estuviera discutiendo a favor o manteniéndolos como una columna, pero los otros párrafos dicen lo contrario.
dfmiller

@dfmiller, tal vez no entendí bien la pregunta, ahora no estoy seguro de si la separación se haría en la consulta o en la tabla. He aclarado la respuesta para que sea más clara.
paxdiablo

Mucho mejor. Nunca consideré usar una consulta de selección excepto para actualizar la base de datos. Esa sería una idea terrible.
dfmiller

7

utilizar este

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

Esto tomará la primera y la última subcadena delimitada por espacios del campo, que no funciona en todas las circunstancias. Por ejemplo, si el campo de nombre es "Lilly von Schtupp", obtendrá 'Lilly', 'Schtupp' como primer nombre, apellido.
John Franklin

5

No respondiendo exactamente la pregunta, pero ante el mismo problema terminé haciendo esto:

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

En MySQL está funcionando esta opción:

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

por llevar el resto de la cuerda al segundo campo
M. Faraz

3

El único caso en el que puede desear dicha función es una consulta ACTUALIZAR que alterará su tabla para almacenar Nombre y Apellido en campos separados.

El diseño de la base de datos debe seguir ciertas reglas, y la Normalización de la base de datos es una de las más importantes.


Comentarios innecesarios ya que esto es exactamente lo que pidió el cartel; También es inexacto, ya que hay un millón de veces que puede necesitar dividir una cadena para una mejor normalización. No estoy seguro de por qué o cómo se votó esto.
daticon

Usar índices en campos divididos es casi tan imposible como convertir MySQL en un triturador de hojas, pero eso no impedirá que la gente pregunte al respecto. Buena respuesta: la base de datos DEBE reflejar los datos, no las especificaciones de su triturador de hojas.
HoldOffHunger

2

Tenía una columna donde el nombre y el apellido estaban ambos en una columna. El nombre y apellido estaban separados por una coma. El siguiente código funcionó. NO hay verificación / corrección de errores. Solo una tonta división. Usó phpMyAdmin para ejecutar la instrucción SQL.

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

13.2.10 ACTUALIZAR Sintaxis


1

Esto toma smhg de aquí y los cortes del último índice de una subcadena dada en MySQL y los combina. Esto es para mysql, todo lo que necesitaba era obtener una división de nombre decente de nombre_ apellido_nombre con el apellido una sola palabra, el primer nombre todo antes de esa sola palabra, donde el nombre podría ser nulo, 1 palabra, 2 palabras, o Más de 2 palabras. Es decir: nulo; María; Mary Smith Mary A. Smith; Mary Sue Ellen Smith;

Entonces, si nombre es una palabra o nulo, last_name es nulo. Si el nombre es> 1 palabra, apellido es la última palabra y nombre_todas las palabras anteriores a la última palabra.

Tenga en cuenta que ya he recortado cosas como Joe Smith Jr.; Joe Smith Esq. y así sucesivamente, manualmente, lo cual fue doloroso, por supuesto, pero fue lo suficientemente pequeño como para hacer eso, por lo que debe asegurarse de mirar realmente los datos en el campo de nombre antes de decidir qué método usar.

Tenga en cuenta que esto también recorta el resultado, por lo que no termina con espacios delante o después de los nombres.

Solo estoy publicando esto para otros que podrían buscar en Google buscando lo que necesitaba. Esto funciona, por supuesto, pruébelo primero con el select.

Es una cosa única, así que no me importa la eficiencia.

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

Método que solía dividir first_name en first_name y last_name cuando los datos llegaron todos en el campo first_name. Esto colocará solo la última palabra en el campo de apellido, por lo que "John Phillips Sousa" será "John Phillips" nombre y apellido "Sousa". También evita sobrescribir cualquier registro que ya haya sido arreglado.

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 proporciona una función de división nativa:

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

1
¿Puede proporcionar un enlace a la documentación? Una búsqueda de dev.mysql.com aparece seca. La Sección 12.5 tiene una sugerencia de la comunidad en los comentarios para esta función.
DRaehal
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.