Dividi il valore da un campo a due


125

Ho un campo tabella membernameche contiene sia il cognome che il nome degli utenti. E 'possibile dividere quelli in 2 campi memberfirst, memberlast?

Tutti i record hanno questo formato "Nome Cognome" (senza virgolette e uno spazio in mezzo).


6
"Tutti i record hanno questo formato" Nome Cognome "(senza virgolette e uno spazio in mezzo)." ... miracolosamente ... Per favore , per favore , non dimenticare le persone come me quando prendi le decisioni sul database. Troppo spesso ricevo siti Web che mi dicono che il mio cognome contiene un personaggio illegale (sic) ... :(
Stijn de Witt

@StijndeWitt Hai ragione in generale, tuttavia sembra che questo database non contenga il tuo nome, almeno non nella sua forma ufficiale. Nel mio paese i cognomi sono scritti per primi, quindi dovrei anche essere "discriminato" in questa tabella di dati. Basta vedere questo ->
Dávid Horváth

Risposte:


226

Purtroppo MySQL non dispone di una funzione di stringa divisa. Tuttavia, è possibile creare una funzione definita dall'utente per questo, come quella descritta nel seguente articolo:

Con quella funzione:

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 ;

sarai in grado di creare la tua query come segue:

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

Se preferisci non utilizzare una funzione definita dall'utente e non ti dispiace che la query sia un po 'più dettagliata, puoi anche fare quanto segue:

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

Ottima soluzione per questo problema!
Bergkamp,

non è ancora possibile utilizzare IN come "matrice di valori" da quell'operazione suddivisa?
Miguel,

3
L'uso del LENGTHmultibyte è sicuro? "LENGTH (str): restituisce la lunghezza della stringa str, misurata in byte. Un carattere multibyte conta come byte multipli. Ciò significa che per una stringa contenente cinque caratteri a 2 byte, LENGTH () restituisce 10, mentre CHAR_LENGTH () restituisce 5."
Erk,

Questo non funzionerà correttamente quando si ha a che fare con caratteri multibyte / utf8, come menzionato da @Erk. Solo la soluzione semplice con le due istruzioni SUBSTRING_INDEX funziona con utf8 / multibyte
Michael

LENGTH (), LOCATE () o qualsiasi altra cosa che si basi su un conteggio di posizione fallirà con caratteri multibyte.
Michael,

68

Variante SELECT (non creazione di una funzione definita dall'utente):

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

Questo approccio si occupa anche di:

  • valori del nome membro senza spazio : aggiungerà l'intera stringa a memberfirst e imposta memberlast su NULL.
  • valori del nome membro che hanno più spazi : aggiungerà tutto prima del primo spazio a memberfirst e il resto (compresi gli spazi aggiuntivi) a memberlast.

La versione di AGGIORNAMENTO sarebbe:

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

Sarebbe anche utile vedere come tagliare solo l'ultima parola per cognome, e tutte le non ultime per nome, per esempio: Mary A. Smith, che sono i tipi che devo affrontare in una vecchia tabella db fix. Vedrò se riesco a capirlo e pubblicare il risultato, in caso contrario, se è possibile pubblicare anche quell'opzione per completare la risposta.
Lizardx,

come possiamo lanciarlo a numero intero poiché membername è varchar .. lascia che memberfirst sia di tipo int. Funzionerà se uso direttamente cast ()?
infinitywarior,

Meriti una medaglia.
rpajaziti,

23

Sembra che le risposte esistenti siano troppo complicate o non una risposta rigorosa alla domanda specifica.

Penso che la semplice risposta sia la seguente domanda:

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

Penso che non sia necessario trattare nomi con più di due parole in questa particolare situazione. Se vuoi farlo correttamente, dividere può essere molto difficile o addirittura impossibile in alcuni casi:

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

In un database progettato correttamente, i nomi umani devono essere memorizzati sia in parti che nel loro insieme. Questo non è sempre possibile, ovviamente.


20

Se il tuo piano è di farlo come parte di una query, ti preghiamo di non farlo (a) . Seriamente, è un killer delle prestazioni. Potrebbero verificarsi situazioni in cui non ti interessano le prestazioni (come i processi di migrazione una tantum per dividere i campi consentendo prestazioni migliori in futuro) ma, se lo fai regolarmente per qualsiasi cosa diversa da un database di topolino, tu stai sprecando risorse.

Se ti capita mai di dover elaborare solo una parte di una colonna in qualche modo, la progettazione del tuo DB è difettosa. Potrebbe funzionare bene su una rubrica di casa o su un'applicazione ricetta o su una miriade di altri piccoli database ma non sarà scalabile su sistemi "reali".

Conservare i componenti del nome in colonne separate. È quasi sempre molto più veloce unire le colonne con una semplice concatenazione (quando è necessario il nome completo) piuttosto che dividerle con una ricerca di caratteri.

Se, per qualche motivo, non è possibile dividere il campo, inserire almeno le colonne aggiuntive e utilizzare un trigger di inserimento / aggiornamento per popolarle. Sebbene non sia 3NF, questo garantirà che i dati siano ancora coerenti e accelererà enormemente le tue query. Puoi anche assicurarti che le colonne extra siano maiuscole (e indicizzate se le stai cercando) allo stesso tempo in modo da non dover giocherellare con i problemi del caso.

E, se non riesci nemmeno ad aggiungere le colonne e i trigger, fai attenzione (e rendi consapevole il tuo client, se è per un client) che non è scalabile.


(a) Ovviamente, se il tuo intento è quello di utilizzare questa query per correggere lo schema in modo che i nomi siano collocati in colonne separate nella tabella anziché nella query, lo considererei un uso valido. Ma lo ribadisco, farlo nella query non è davvero una buona idea.


4
A volte, devi farlo. Perché ne ho bisogno in uno script di migrazione, quindi non mi importa delle performance.
Matthieu Napoli,

@dfmiller, sì, l'ho fatto, quindi la mia risposta motivata e dettagliata, e grazie per l'interesse. Se hai un problema specifico con qualcosa che ho scritto, segnalalo e vedrò se può essere migliorato. Il tuo commento attuale è praticamente inutile nel migliorare la situazione, se questo era davvero il tuo intento. O forse ti piace semplicemente inviare commenti casuali in rete, è difficile da dire :-) Sono d'accordo con la risposta, ovviamente, l'accesso sottocolonnare non è scalabile ed è quasi sempre una cattiva idea, a meno che non sia utilizzato allo scopo di effettivamente riparando l' accesso sub-colonnare.
paxdiablo,

3
La domanda è come dividere la singola colonna in 2 e quindi rispondere dicendo "Non farlo" e quindi procedere per spiegare perché dovrebbero essere divisi. Il tuo primo paragrafo sembra che tu stia discutendo a favore o mantenerli come una colonna, ma gli altri paragrafi dicono il contrario.
dfmiller,

@dfmiller, forse ho frainteso la domanda, non sono sicuro ora se la separazione debba essere fatta nella query o nella tabella. Ho chiarito la risposta per renderla più chiara.
paxdiablo,

Molto meglio. Non ho mai preso in considerazione l'utilizzo di una query di selezione se non per aggiornare il database. Sarebbe un'idea terribile.
dfmiller,

7

Usa questo

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

Questo afferrerà la sottostringa delimitata dal primo e dall'ultimo spazio dal campo, che non funziona in tutte le circostanze. Ad esempio, se il campo del nome è "Lilly von Schtupp", otterrai "Lilly", "Schtupp" come nome, cognome.
John Franklin,

5

Non rispondendo esattamente alla domanda, ma di fronte allo stesso problema ho finito per fare questo:

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

In MySQL funziona questa opzione:

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

per portare il resto della corda nel secondo campo
M. Faraz,

3

L'unico caso in cui potresti desiderare una tale funzione è una query UPDATE che modificherà la tua tabella per archiviare Nome e Cognome in campi separati.

La progettazione del database deve seguire alcune regole e la normalizzazione del database è tra le più importanti


Commento non necessario in quanto questo è esattamente ciò che il poster ha richiesto; anche impreciso in quanto vi sono milioni di volte che potresti dover dividere una stringa per la migliore normalizzazione. Non so perché o come sia mai stato votato.
daticon,

Usare gli indici su campi divisi è quasi impossibile quanto trasformare MySQL in una trinciatrice per foglie, ma ciò non impedirà alle persone di chiederlo. Buona risposta: il database DOVREBBE riflettere i dati, non le specifiche del mulino per foglie.
HoldOffHunger

2

Avevo una colonna in cui il nome e il cognome erano entrambi in una colonna. Il nome e il cognome erano separati da una virgola. Il codice seguente ha funzionato. Non ci sono errori di controllo / correzione. Solo una stupida spaccatura. Usato phpMyAdmin per eseguire l'istruzione SQL.

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

13.2.10 Sintassi di AGGIORNAMENTO


1

Questo prende smhg da qui e bruscamente dall'ultimo indice di una determinata sottostringa in MySQL e li combina. Questo è per mysql, tutto ciò di cui avevo bisogno era ottenere una discreta suddivisione del nome in first_name last_name con il cognome una sola parola, il nome tutto prima di quella singola parola, dove il nome poteva essere nullo, 1 parola, 2 parole o più di 2 parole. Vale a dire: null; Maria; Mary Smith; Mary A. Smith; Mary Sue Ellen Smith;

Quindi, se name è una parola o null, last_name è null. Se il nome è> 1 parola, last_name è l'ultima parola e first_name tutte le parole prima dell'ultima parola.

Nota che ho già eliminato cose come Joe Smith Jr.; Joe Smith Esq. e così via, manualmente, il che è stato doloroso, ovviamente, ma era abbastanza piccolo per farlo, quindi vuoi assicurarti di guardare davvero i dati nel campo del nome prima di decidere quale metodo usare.

Si noti che questo riduce anche il risultato, quindi non si finisce con spazi davanti o dopo i nomi.

Sto solo pubblicando questo per gli altri che potrebbero andare su Google a cercare ciò di cui avevo bisogno. Questo funziona, ovviamente, testalo prima con il select.

È una cosa sola, quindi non mi interessa l'efficienza.

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

Metodo che ho usato per dividere first_name in first_name e last_name quando i dati sono arrivati ​​tutti nel campo first_name. Questo metterà solo l'ultima parola nel campo del cognome, quindi "john phillips sousa" sarà il nome "john phillips" e il cognome "sousa". Evita inoltre di sovrascrivere tutti i record che sono già stati corretti.

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 fornisce una funzione split nativa:

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

1
Potete fornire un link alla documentazione? Una ricerca su dev.mysql.com risulta asciutta. La sezione 12.5 contiene un suggerimento della comunità nei commenti per questa funzione.
DRaehal,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.