Teilen Sie den Wert von einem Feld auf zwei

125

Ich habe ein Tabellenfeld, membernamedas sowohl den Nachnamen als auch den Vornamen der Benutzer enthält. Ist es möglich , diese in zwei Felder aufgeteilt memberfirst, memberlast?

Alle Datensätze haben das Format "Vorname Nachname" (ohne Anführungszeichen und Leerzeichen dazwischen).

Tsiger
quelle
6
"Alle Datensätze haben das Format" Vorname Nachname "(ohne Anführungszeichen und ein Leerzeichen dazwischen)." ... auf wundersame Weise ... Bitte, bitte , vergessen Sie nicht Leute wie mich, wenn Sie Datenbankentscheidungen treffen. Zu oft bekomme ich Websites, die mir sagen, dass mein Nachname ein illegales (sic) Zeichen enthält ... :(
Stijn de Witt
@StijndeWitt Sie haben im Allgemeinen Recht, aber es scheint, dass diese Datenbank Ihren Namen nicht enthält, zumindest nicht in ihrer offiziellen Form. In meinem Land werden die Nachnamen zuerst geschrieben, daher würde ich in dieser Datentabelle auch "diskriminiert". Sehen Sie einfach das ->
Dávid Horváth

Antworten:

226

Leider bietet MySQL keine Split-String-Funktion. Sie können hierfür jedoch eine benutzerdefinierte Funktion erstellen , wie die im folgenden Artikel beschriebene:

Mit dieser Funktion:

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 ;

Sie können Ihre Abfrage wie folgt erstellen:

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

Wenn Sie keine benutzerdefinierte Funktion verwenden möchten und die Abfrage nicht etwas ausführlicher ist, können Sie auch Folgendes tun:

SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(membername, ' ', 1), ' ', -1) as memberfirst,
       SUBSTRING_INDEX(SUBSTRING_INDEX(membername, ' ', 2), ' ', -1) as memberlast
FROM   users;
Daniel Vassallo
quelle
Tolle Lösung für dieses Problem!
Bergkamp
Sie können das IN dennoch nicht als "Array von Werten" aus dieser Aufteilungsoperation verwenden?
Miguel
3
Ist Ihre Verwendung von LENGTHMultibyte sicher? "LENGTH (str): Gibt die Länge des Strings str zurück, gemessen in Bytes. Ein Multibyte-Zeichen zählt als mehrere Bytes. Dies bedeutet, dass LENGTH () für einen String mit fünf 2-Byte-Zeichen 10 zurückgibt, während CHAR_LENGTH () zurückgibt 5. "
Erk
Dies funktioniert nicht richtig, wenn mit Multibyte / utf8-Zeichen gearbeitet wird, wie @Erk erwähnt. Nur die einfache Lösung mit den beiden SUBSTRING_INDEX-Anweisungen funktioniert mit utf8 / multibyte
Michael
LENGTH (), LOCATE () oder alles, was auf einer Positionszählung beruht, schlägt mit Multibyte-Zeichen fehl.
Michael
68

SELECT-Variante (keine benutzerdefinierte Funktion erstellen):

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

Dieser Ansatz kümmert sich auch um:

  • Mitgliedsname Werte ohne Leerzeichen : Es wird die gesamte Zeichenfolge zu memberfirst hinzugefügt und memberlast auf NULL gesetzt.
  • Mitgliedsnamenwerte mit mehreren Leerzeichen : Es wird alles vor dem ersten Leerzeichen zu memberfirst und der Rest (einschließlich zusätzlicher Leerzeichen) zu memberlast hinzugefügt.

Die UPDATE-Version wäre:

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
    );
smhg
quelle
Es wäre auch nützlich zu sehen, wie man nur das letzte Wort für den Nachnamen und alle nicht letzten für den Vornamen abschneidet, zum Beispiel: Mary A. Smith, die Typen, mit denen ich mich in einer alten Datenbank-Tabelle befassen muss Fix. Ich werde sehen, ob ich es herausfinden und das Ergebnis veröffentlichen kann, wenn nicht, wenn Sie diese Option auch veröffentlichen können, würde dies Ihre Antwort vervollständigen.
Lizardx
Wie können wir es in eine Ganzzahl umwandeln, da der Mitgliedsname varchar ist? Lassen Sie memberfirst vom Typ int sein. Funktioniert es, wenn ich cast () direkt verwende?
Infinitywarior
Sie, mein Herr, verdienen eine Medaille.
Rpajaziti
23

Es scheint, dass bestehende Antworten zu kompliziert oder keine strenge Antwort auf die jeweilige Frage sind.

Ich denke, die einfache Antwort ist die folgende Frage:

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

Ich denke, es ist in dieser besonderen Situation nicht notwendig, mit Namen mit mehr als zwei Wörtern umzugehen. Wenn Sie es richtig machen möchten, kann das Teilen in einigen Fällen sehr schwierig oder sogar unmöglich sein:

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

In einer ordnungsgemäß gestalteten Datenbank sollten menschliche Namen sowohl in Teilen als auch als Ganzes gespeichert werden. Dies ist natürlich nicht immer möglich.

Dávid Horváth
quelle
20

Wenn Ihr Plan ist dies als Teil eines tun Abfrage, bitte nicht tun , dass (a) . Im Ernst, es ist ein Performance-Killer. Es kann Situationen geben, in denen Sie sich nicht für die Leistung interessieren (z. B. einmalige Migrationsjobs zum Aufteilen der Felder, um in Zukunft eine bessere Leistung zu erzielen). Wenn Sie dies jedoch regelmäßig für etwas anderes als eine Mickey-Mouse-Datenbank tun, sind Sie es verschwenden Ressourcen.

Wenn Sie jemals nur einen Teil einer Spalte auf irgendeine Weise verarbeiten müssen, ist Ihr DB-Design fehlerhaft. Es funktioniert möglicherweise einwandfrei in einem Adressbuch oder einer Rezeptanwendung oder in einer Vielzahl anderer kleiner Datenbanken, ist jedoch nicht auf "echte" Systeme skalierbar.

Speichern Sie die Komponenten des Namens in separaten Spalten. Es ist fast immer viel schneller, Spalten mit einer einfachen Verkettung (wenn Sie den vollständigen Namen benötigen) zusammenzufügen, als sie mit einer Zeichensuche aufzuteilen.

Wenn Sie das Feld aus irgendeinem Grund nicht teilen können, fügen Sie zumindest die zusätzlichen Spalten ein und füllen Sie sie mit einem Einfüge- / Aktualisierungsauslöser. Dies ist zwar nicht 3NF, garantiert jedoch, dass die Daten immer noch konsistent sind, und beschleunigt Ihre Abfragen massiv. Sie können auch sicherstellen, dass die zusätzlichen Spalten gleichzeitig in Kleinbuchstaben (und indiziert, wenn Sie danach suchen) angezeigt werden, damit Sie nicht mit Fallproblemen herumspielen müssen.

Wenn Sie nicht einmal die Spalten und Trigger hinzufügen können, beachten Sie (und machen Sie Ihren Client darauf aufmerksam, wenn es sich um einen Client handelt), dass er nicht skalierbar ist.


(a) Wenn Sie beabsichtigen, diese Abfrage zum Korrigieren des Schemas zu verwenden, sodass die Namen in separaten Spalten in der Tabelle und nicht in der Abfrage platziert werden, würde ich dies als gültige Verwendung betrachten. Aber ich wiederhole, es in der Abfrage zu tun, ist keine wirklich gute Idee.

paxdiablo
quelle
4
Manchmal muss man es tun. Wenn ich es in einem Migrationsskript brauche, interessieren mich Performances nicht.
Matthieu Napoli
@dfmiller, ja, ich habe es getan, daher meine begründete und detaillierte Antwort, und danke für Ihr Interesse. Wenn Sie ein bestimmtes Problem mit etwas haben, das ich geschrieben habe, weisen Sie darauf hin, und ich werde sehen, ob es verbessert werden kann. Ihr aktueller Kommentar ist für die Verbesserung der Situation so gut wie nutzlos, wenn dies tatsächlich Ihre Absicht war. Oder vielleicht nur Sie wie speienden zufällige Kommentare im Netz, ist es schwer zu sagen :-) ich von der Antwort stehen, natürlich, Untersäulen Zugang ist nicht skalierbar und ist fast immer eine schlechte Idee, es sei denn , es zum Zwecke der verwendet wird tatsächlich Befestigungsunter kolumnare Zugang.
Paxdiablo
3
Die Frage ist, wie Sie die einzelne Spalte in zwei Spalten aufteilen können. Anschließend antworten Sie mit "Nicht dazu" und erklären anschließend, warum sie aufgeteilt werden sollten. Ihr erster Absatz klingt so, als würden Sie dafür argumentieren oder sie als eine Spalte behalten, aber die anderen Absätze sagen das Gegenteil.
Dfmiller
@dfmiller, vielleicht habe ich die Frage falsch verstanden, ich bin mir jetzt nicht sicher, ob die Trennung in der Abfrage oder in der Tabelle erfolgen soll. Ich habe die Antwort geklärt, um sie hoffentlich klarer zu machen.
Paxdiablo
Viel besser. Ich habe nie in Betracht gezogen, eine Auswahlabfrage zu verwenden, außer um die Datenbank zu aktualisieren. Das wäre eine schreckliche Idee.
Dfmiller
7

benutze das

SELECT SUBSTRING_INDEX(SUBSTRING_INDEX( `membername` , ' ', 2 ),' ',1) AS b, 
SUBSTRING_INDEX(SUBSTRING_INDEX( `membername` , ' ', -1 ),' ',2) AS c FROM `users` WHERE `userid`='1'
Karthik
quelle
Dadurch werden der erste und der letzte durch Leerzeichen getrennte Teilstring aus dem Feld entfernt, was nicht unter allen Umständen funktioniert. Wenn das Namensfeld beispielsweise "Lilly von Schtupp" lautet, erhalten Sie "Lilly", "Schtupp" als Vor- und Nachname.
John Franklin
5

Ich habe die Frage nicht genau beantwortet, aber mit dem gleichen Problem konfrontiert, dass ich dies getan habe:

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
Commonpike
quelle
4

In MySQL funktioniert diese Option:

SELECT Substring(nameandsurname, 1, Locate(' ', nameandsurname) - 1) AS 
       firstname, 
       Substring(nameandsurname, Locate(' ', nameandsurname) + 1)    AS lastname 
FROM   emp  
Irakli Kardava
quelle
für den Rest der Saite in das zweite Feld
M. Faraz
3

Der einzige Fall, in dem Sie eine solche Funktion wünschen, ist eine UPDATE-Abfrage, die Ihre Tabelle so ändert, dass Vorname und Nachname in separaten Feldern gespeichert werden.

Das Datenbankdesign muss bestimmten Regeln folgen, und die Datenbanknormalisierung gehört zu den wichtigsten

Ihr gesunder Menschenverstand
quelle
Unnötiger Kommentar, da dies genau das ist, wonach das Poster gefragt hat. Dies ist auch ungenau, da Sie möglicherweise eine Million Mal eine Zeichenfolge teilen müssen, um eine optimale Normalisierung zu erzielen. Unsicher, warum oder wie dies jemals abgestimmt wurde.
Daticon
Die Verwendung von Indizes für geteilte Felder ist ungefähr so ​​unmöglich wie die Umwandlung von MySQL in einen Blattmulcher, aber das hindert die Leute nicht daran, danach zu fragen. Gute Antwort - Datenbank SOLLTE die Daten widerspiegeln, nicht Ihre Laubmulcher-Spezifikationen.
HoldOffHunger
2

Ich hatte eine Spalte, in der sich Vor- und Nachname in einer Spalte befanden. Der Vor- und Nachname wurden durch ein Komma getrennt. Der folgende Code hat funktioniert. Es erfolgt KEINE Fehlerprüfung / -korrektur. Nur eine blöde Trennung. Verwendet phpMyAdmin, um die SQL-Anweisung auszuführen.

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

13.2.10 UPDATE-Syntax

Steve R.
quelle
1

Dies dauert smhg von hier und Curt's von letzten Index eines bestimmten Teilstrings in MySQL und kombiniert sie. Dies ist für MySQL. Alles, was ich brauchte, war eine anständige Aufteilung des Namens in Vorname Nachname mit dem Nachnamen ein einzelnes Wort, der Vorname alles vor diesem einzelnen Wort, wobei der Name null, 1 Wort, 2 Wörter oder sein könnte mehr als 2 Wörter. Dh: Null; Maria; Mary Smith; Mary A. Smith; Mary Sue Ellen Smith;

Wenn der Name also ein Wort oder null ist, ist der Nachname null. Wenn der Name> 1 Wort ist, ist der Nachname das letzte Wort und der Vorname alle Wörter vor dem letzten Wort.

Beachten Sie, dass ich bereits Dinge wie Joe Smith Jr. abgeschnitten habe. Joe Smith Esq. und so weiter, manuell, was natürlich schmerzhaft war, aber es war klein genug, um dies zu tun. Sie sollten also sicherstellen, dass Sie sich die Daten im Namensfeld wirklich ansehen, bevor Sie sich für eine Methode entscheiden.

Beachten Sie, dass dadurch auch das Ergebnis gekürzt wird, sodass Sie keine Leerzeichen vor oder nach den Namen erhalten.

Ich poste dies nur für andere, die sich hierher googeln und nach dem suchen, was ich brauche. Dies funktioniert natürlich, testen Sie es zuerst mit der Auswahl.

Es ist eine einmalige Sache, daher ist mir Effizienz egal.

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
    ) 
);
Lizardx
quelle
0

Methode Ich habe Vorname in Vorname und Nachname aufgeteilt, als die Daten alle im Feld Vorname ankamen. Dadurch wird nur das Nachwort in das Feld für den Nachnamen eingefügt, sodass "John Phillips Sousa" der Vorname "John Phillips" und der Nachname "Sousa" ist. Außerdem wird vermieden, dass bereits reparierte Datensätze überschrieben werden.

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
TheSatinKnight
quelle
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
    );
Aniket
quelle
-3

MySQL 5.4 bietet eine native Split-Funktion:

SPLIT_STR(<column>, '<delimiter>', <index>)
emfi
quelle
1
Können Sie einen Link zur Dokumentation bereitstellen? Eine Suche auf dev.mysql.com ist trocken. Abschnitt 12.5 enthält in den Kommentaren zu dieser Funktion einen Community-Vorschlag.
DRaehal