Podziel wartość z jednego pola na dwa


125

Mam pole tabeli, membernamektóre zawiera zarówno nazwisko, jak i imię użytkownika. Czy to możliwe, aby podzielić te na 2 pola memberfirst, memberlast?

Wszystkie rekordy mają format „Imię Nazwisko” (bez cudzysłowów i spacji pomiędzy).


6
„Wszystkie rekordy mają format„ Imię Nazwisko ”(bez cudzysłowów i spacji pomiędzy).” ... cudownie ... Proszę, proszę , nie zapominaj o takich ludziach jak ja przy podejmowaniu decyzji dotyczących bazy danych. Zbyt często dostaję strony internetowe, które mówią mi, że moje nazwisko zawiera niedozwolony (sic) znak ... :(
— Stijn de Witt

@StijndeWitt Generalnie masz rację, jednak wygląda na to, że ta baza danych nie zawiera Twojego nazwiska, przynajmniej nie w swojej oficjalnej formie. W moim kraju nazwiska są zapisywane jako pierwsze, więc ja również byłbym „dyskryminowany” w tej tabeli danych. Zobacz to ->
— Dávid Horváth,

Odpowiedzi:


226

Niestety MySQL nie posiada funkcji podziału łańcucha. Możesz jednak utworzyć w tym celu funkcję zdefiniowaną przez użytkownika, taką jak ta opisana w następującym artykule:

Dzięki tej funkcji:

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 ;

możesz zbudować zapytanie w następujący sposób:

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

Jeśli wolisz nie używać funkcji zdefiniowanej przez użytkownika i nie masz nic przeciwko, aby zapytanie było nieco bardziej szczegółowe, możesz również wykonać następujące czynności:

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

Świetne rozwiązanie tego problemu!
— Bergkamp

nadal nie możesz użyć IN jako „tablicy wartości” z tej operacji podziału?
— Miguel

3
Czy korzystanie z LENGTHwielobajtów jest bezpieczne? „LENGTH (str): Zwraca długość ciągu znaków mierzoną w bajtach. Znak wielobajtowy jest liczony jako wiele bajtów. Oznacza to, że dla ciągu zawierającego pięć znaków 2-bajtowych LENGTH () zwraca 10, natomiast CHAR_LENGTH () zwraca 5. "
— Erk

Jak wspomniał @Erk, nie będzie to działać poprawnie w przypadku znaków wielobajtowych / utf8. Tylko proste rozwiązanie z dwoma instrukcjami SUBSTRING_INDEX działa z utf8 / multibyte
— Michael

LENGTH (), LOCATE () lub cokolwiek, co zależy od liczby pozycji, zakończy się niepowodzeniem ze znakami wielobajtowymi.
— Michael

68

Wariant SELECT (bez tworzenia funkcji zdefiniowanej przez użytkownika):

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

Takie podejście dba również o:

  • wartości membername bez spacji : doda cały ciąg do memberfirst i ustawi element memberlast na NULL.
  • wartości membername , które mają wiele spacji : doda wszystko przed pierwszą spacją do memberfirst, a resztę (w tym dodatkowe spacje) do memberlast.

Wersja UPDATE to:

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

Przydatne byłoby również zobaczenie, jak odciąć tylko ostatnie słowo dla nazwiska i wszystkie inne niż ostatnie dla imienia, na przykład: Mary A. Smith, czyli typy, z którymi mam do czynienia w starej tabeli db naprawić. Zobaczę, czy uda mi się to rozgryźć i opublikuję wynik, jeśli nie, czy możesz również opublikować tę opcję, aby Twoja odpowiedź była kompletna.
— Lizardx

jak możemy rzutować to na liczbę całkowitą, skoro nazwa_członka to varchar .. niech memberfirst będzie typu int. Czy zadziała, jeśli bezpośrednio użyję cast ()?
— infinitywarior

Pan zasługuje na medal.
— rpajaziti

23

Wydaje się, że istniejące odpowiedzi są zbyt skomplikowane lub nie stanowią ścisłej odpowiedzi na dane pytanie.

Myślę, że prosta odpowiedź brzmi:

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

Myślę, że w tej konkretnej sytuacji nie jest konieczne zajmowanie się nazwami składającymi się z więcej niż dwóch słów. Jeśli chcesz to zrobić poprawnie, w niektórych przypadkach rozłupywanie może być bardzo trudne lub nawet niemożliwe:

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

W odpowiednio zaprojektowanej bazie danych nazwiska ludzkie powinny być przechowywane zarówno w częściach, jak iw całości. Oczywiście nie zawsze jest to możliwe.


20

Jeśli plan jest zrobić to jako część zapytania, proszę nie robić (a) . Poważnie, to zabójca wydajności. Mogą wystąpić sytuacje, w których nie zależy Ci na wydajności (na przykład jednorazowe zadania migracji, aby podzielić pola, aby uzyskać lepszą wydajność w przyszłości), ale jeśli robisz to regularnie dla czegokolwiek innego niż baza danych myszy miki, marnujemy zasoby.

Jeśli kiedykolwiek będziesz musiał przetworzyć tylko część kolumny w jakiś sposób, twój projekt bazy danych jest wadliwy. Może dobrze działać w domowej książce adresowej, aplikacji z przepisami lub w dowolnej z wielu innych małych baz danych, ale nie da się jej skalować do „prawdziwych” systemów.

Przechowuj składniki nazwy w oddzielnych kolumnach. Łączenie kolumn za pomocą prostej konkatenacji (gdy potrzebujesz pełnego imienia i nazwiska) jest prawie zawsze o wiele szybsze niż dzielenie ich za pomocą wyszukiwania znakowego.

Jeśli z jakiegoś powodu nie możesz podzielić pola, przynajmniej umieść dodatkowe kolumny i użyj wyzwalacza wstawiania / aktualizowania, aby je wypełnić. Chociaż nie jest to 3NF, zagwarantuje to, że dane są nadal spójne i znacznie przyspieszy twoje zapytania. Możesz również upewnić się, że dodatkowe kolumny są pisane małymi literami (i indeksowane, jeśli ich szukasz) w tym samym czasie, aby nie musieć majstrować przy problemach ze wielkością liter.

A jeśli nie możesz nawet dodać kolumn i wyzwalaczy, pamiętaj (i uświadom swojego klienta, jeśli jest przeznaczony dla klienta), że nie jest skalowalny.


(a) Oczywiście, jeśli zamierzasz użyć tego zapytania do naprawienia schematu, tak aby nazwy były umieszczane w oddzielnych kolumnach w tabeli, a nie w zapytaniu, uważam to za prawidłowe użycie. Ale powtarzam, robienie tego w zapytaniu nie jest dobrym pomysłem.


4
Czasami musisz to zrobić. Potrzebuje tego w skrypcie migracji, więc nie obchodzą mnie występy.
— Matthieu Napoli

@dfmiller, tak, zrobiłem, stąd moja uzasadniona i szczegółowa odpowiedź i dziękuję za zainteresowanie. Jeśli masz konkretny problem z czymś, co napisałem, wskaż to, a zobaczę, czy można to poprawić. Twój obecny komentarz jest praktycznie bezużyteczny w poprawianiu sytuacji, jeśli rzeczywiście taki był Twój zamiar. A może po prostu lubisz wyrzucać przypadkowe komentarze w sieci, trudno powiedzieć :-) Nie zgadzam się z odpowiedzią, oczywiście, dostęp subkolumnowy nie jest skalowalny i prawie zawsze jest złym pomysłem, chyba że jest używany do celów faktycznie naprawiający dostęp do kolumn.
— paxdiablo

3
Pytanie brzmi, jak podzielić pojedynczą kolumnę na dwie, a następnie odpowiadasz, mówiąc „Nie rób tego”, a następnie przystępujesz do wyjaśniania, dlaczego należy je podzielić. Twój pierwszy akapit brzmi tak, jakbyś się spierał lub trzymał je w jednej kolumnie, ale pozostałe akapity mówią coś przeciwnego.
— dfmiller

@dfmiller, być może źle zrozumiałem pytanie, nie jestem teraz pewien, czy separacja miała być dokonana w zapytaniu, czy w tabeli. Wyjaśniłem odpowiedź, miejmy nadzieję, że będzie jaśniejsza.
— paxdiablo

Dużo lepiej. Nigdy nie rozważałem używania zapytania wybierającego z wyjątkiem aktualizacji bazy danych. To byłby okropny pomysł.
— dfmiller

7

Użyj tego

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

Spowoduje to pobranie pierwszego i ostatniego podciągu rozdzielanego spacjami z pola, co nie działa w każdych okolicznościach. Na przykład, jeśli pole nazwiska to „Lilly von Schtupp”, otrzymasz „Lilly”, „Schtupp” jako imię i nazwisko.
— John Franklin,

5

Nie do końca odpowiadając na pytanie, ale napotkałem ten sam problem, który skończyłem:

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

W MySQL działa ta opcja:

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

za zabranie reszty struny na drugie pole
— M. Faraz

3

Jedynym przypadkiem, w którym możesz chcieć takiej funkcji, jest zapytanie UPDATE, które zmieni tabelę tak, aby przechowywała Imię i Nazwisko w oddzielnych polach.

Projekt bazy danych musi przestrzegać pewnych reguł, a normalizacja bazy danych jest jedną z najważniejszych


Niepotrzebny komentarz, ponieważ właśnie o to prosił plakat; również niedokładne, ponieważ istnieje milion razy, gdy trzeba będzie podzielić ciąg w celu uzyskania najlepszej normalizacji. Nie wiem, dlaczego ani jak to kiedykolwiek zostało przegłosowane.
— daticon

Używanie indeksów na podzielonych polach jest prawie tak niemożliwe, jak przekształcenie MySQL w mulczera do liści, ale to nie powstrzyma ludzi przed pytaniem o to. Dobra odpowiedź - baza danych POWINNA odzwierciedlać dane, a nie specyfikacje rozdrabniacza do liści.
— HoldOffHunger,

2

Miałem kolumnę, w której imię i nazwisko znajdowały się w jednej kolumnie. Imię i nazwisko oddzielone przecinkiem. Poniższy kod zadziałał. NIE ma sprawdzania / korygowania błędów. Tylko głupi rozłam. Użył phpMyAdmin do wykonania instrukcji SQL.

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

13.2.10 Składnia UPDATE


1

To pobiera smhg stąd i curt's z ostatniego indeksu danego podciągu w MySQL i łączy je. To jest dla mysql, wszystko, czego potrzebowałem, to uzyskać porządny podział imienia na imię i nazwisko nazwisko z nazwiskiem jedno słowo, imię wszystko przed tym pojedynczym słowem, gdzie nazwa mogłaby być pusta, 1 słowo, 2 słowa lub więcej niż 2 słowa. Tj .: Null; Mary; Mary Smith; Mary A. Smith; Mary Sue Ellen Smith;

Więc jeśli nazwa to jedno słowo lub null, last_name ma wartość null. Jeśli imie jest> 1 słowo, last_name to ostatnie słowo, a first_name to wszystkie słowa przed ostatnim słowem.

Zauważ, że już usunąłem takie rzeczy jak Joe Smith Jr.; Joe Smith Esq. i tak dalej, ręcznie, co było oczywiście bolesne, ale było wystarczająco małe, aby to zrobić, więc przed podjęciem decyzji, której metody użyć, warto sprawdzić dane w polu nazwy.

Zauważ, że to również przycina wynik, więc nie kończysz ze spacjami przed lub po nazwach.

Po prostu publikuję to dla innych, którzy mogą tu znaleźć w Google, szukając tego, czego potrzebowałem. To oczywiście działa, najpierw przetestuj go z zaznaczeniem.

To jednorazowa sprawa, więc nie obchodzi mnie wydajność.

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

Metoda, której użyłem do podzielenia first_name na first_name i last_name, gdy dane dotarły do ​​pola first_name. Spowoduje to umieszczenie tylko ostatniego słowa w polu nazwiska, więc „john phillips sousa” będzie oznaczać „john phillips” imię i „sousa” nazwisko. Pozwala to również uniknąć nadpisywania rekordów, które zostały już naprawione.

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 zapewnia natywną funkcję podziału:

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

1
Czy możesz podać link do dokumentacji. Wyszukiwanie dev.mysql.com wysycha. Sekcja 12.5 zawiera sugestie społeczności w komentarzach dotyczących tej funkcji.
— DRaehal
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.